Skip to content

Commit

Permalink
Add new /admin/users endpoint (#589)
Browse files Browse the repository at this point in the history
Add new /admin/users endpoint

---------

Co-authored-by: Anna Westland-Tegart <[email protected]>
  • Loading branch information
lfilmeyer and aTegart authored Aug 7, 2024
1 parent c68eacb commit ba3fe5d
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 3 deletions.
38 changes: 38 additions & 0 deletions backend/app/controllers/admin/users_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

module Admin
class UsersController < ApplicationController
before_action :prepare_user_params, only: :create

def create
@user = User.new(user_params)
authorize @user

respond_to do |format|
if @user.save
format.json { render json: @user, status: :created }
else
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end

def user_params
surveyor_params = %i[city email firstname lastname phone state street_address zipcode]

params.require(:user).permit(:email, :role, surveyor_attributes: surveyor_params)
end

def prepare_user_params
return if params[:user].blank?

params[:user][:surveyor_attributes] = params[:user].delete(:surveyor)

# For now we have email on User and Surveyor, which we are unsure yet if it is redundant
# In the meantime, copy email from User onto Surveyor
return if params[:user][:surveyor_attributes].blank?

params[:user][:surveyor_attributes][:email] ||= params[:user][:email]
end
end
end
11 changes: 10 additions & 1 deletion backend/app/models/surveyor.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
# frozen_string_literal: true

class Surveyor < ApplicationRecord
STATUS_ACTIVE = 'active'

belongs_to :user
has_and_belongs_to_many :assignments

after_initialize :set_default_status, if: :new_record?

validates :firstname, presence: true
validates :lastname, presence: true
validates :email, presence: true
validates :phone, presence: true
validates :street_address, presence: true
validates :geocode, presence: true
validates :city, presence: true
validates :zipcode, presence: true
validates :state, presence: true
validates :status, presence: true

private

def set_default_status
self.status ||= STATUS_ACTIVE
end
end
8 changes: 8 additions & 0 deletions backend/app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ class User < ApplicationRecord
has_one :surveyor, dependent: :destroy
enum role: { user: 0, surveyor: 1, admin: 2 }
after_initialize :set_default_role, if: :new_record?
after_initialize :set_random_password, if: :new_record?
accepts_nested_attributes_for :surveyor

def set_default_role
self.role ||= :user
Expand All @@ -22,4 +24,10 @@ def jwt_payload
'surveyorId' => surveyor&.id
}
end

private

def set_random_password
self.password ||= SecureRandom.base64(15)
end
end
7 changes: 7 additions & 0 deletions backend/app/policies/user_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class UserPolicy < ApplicationPolicy
def create?
user&.admin?
end
end
27 changes: 25 additions & 2 deletions backend/config/brakeman.ignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
{
"ignored_warnings": [
{
"warning_type": "Mass Assignment",
"warning_code": 105,
"fingerprint": "435e540ce53e70774e6d8dea57d81c95ea0a0c06ae0ce1784ba72b31f3cab4d7",
"check_name": "PermitAttributes",
"message": "Potentially dangerous key allowed for mass assignment",
"file": "app/controllers/admin/users_controller.rb",
"line": 21,
"link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
"code": "params.require(:user).permit(:email, :role, :surveyor_attributes => ([:city, :email, :firstname, :lastname, :phone, :state, :street_address, :zipcode]))",
"render_path": null,
"location": {
"type": "method",
"class": "Admin::UsersController",
"method": "user_params"
},
"user_input": ":role",
"confidence": "Medium",
"cwe_id": [
915
],
"note": ""
},
{
"warning_type": "Mass Assignment",
"warning_code": 105,
Expand Down Expand Up @@ -47,6 +70,6 @@
"note": ""
}
],
"updated": "2023-07-25 20:57:41 -0400",
"brakeman_version": "5.4.1"
"updated": "2024-07-26 14:10:58 -0400",
"brakeman_version": "6.1.2"
}
4 changes: 4 additions & 0 deletions backend/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@
passwords: 'users/passwords'
}, defaults: { format: :json }
root 'homes#index'

namespace :admin do
resources :users, only: [:create]
end
end
124 changes: 124 additions & 0 deletions backend/spec/requests/admin/users_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe '/admin/users', type: :request do
include Devise::Test::IntegrationHelpers

let(:admin) do
User.create(email: '[email protected]', password: 'password', role: :admin)
end

describe 'POST /create' do
context 'with valid parameters for a surveyor' do
let(:valid_attributes) do
{
email: '[email protected]',
role: 'surveyor',
surveyor: {
city: 'Boston',
firstname: 'Alice',
lastname: 'Smith',
phone: '1234567890',
state: 'MA',
street_address: '1 Main St',
zipcode: '02110'
}
}
end

before { sign_in admin }

it 'creates a new user who is a surveyor' do
expect do
post admin_users_url, params: { user: valid_attributes }, as: :json
end.to change(User, :count).by(1)

user = User.last
expect(user.email).to eq('[email protected]')
expect(user.role).to eq('surveyor')
expect(user.surveyor).to be_present

surveyor = user.surveyor
expect(surveyor.city).to eq('Boston')
expect(surveyor.firstname).to eq('Alice')
expect(surveyor.lastname).to eq('Smith')
expect(surveyor.phone).to eq('1234567890')
expect(surveyor.state).to eq('MA')
expect(surveyor.street_address).to eq('1 Main St')
expect(surveyor.zipcode).to eq('02110')
end

it 'redirects to the created user' do
post admin_users_url, params: { user: valid_attributes }, as: :json
expect(response).to have_http_status(:created)
end
end

context 'with valid parameters for an admin' do
let(:valid_attributes) do
{
email: '[email protected]',
role: 'admin'
}
end

before { sign_in admin }

it 'creates a new user who is an admin' do
expect do
post admin_users_url, params: { user: valid_attributes }, as: :json
end.to change(User, :count).by(1)

user = User.last
expect(user.email).to eq('[email protected]')
expect(user.role).to eq('admin')
end

it 'redirects to the created user' do
post admin_users_url, params: { user: valid_attributes }, as: :json
expect(response).to have_http_status(:created)
end
end

context 'with invalid parameters' do
let(:invalid_attributes) do
{
email: '',
role: 'admin'
}
end

before { sign_in admin }

it 'does not create a new user' do
expect do
post admin_users_url, params: { user: invalid_attributes }, as: :json
end.to change(User, :count).by(0)
end

it 'renders a response with 422 status' do
post admin_users_url, params: { user: invalid_attributes }, as: :json
expect(response).to have_http_status(:unprocessable_entity)

error_message = JSON.parse(response.body)['email'][0]
expect(error_message).to eq("can't be blank")
end
end

context 'as unauthorized user' do
let(:valid_attributes) do
{
email: '[email protected]',
role: 'admin'
}
end

it 'raises an error' do
expect do
post admin_users_url, params: { user: valid_attributes }, as: :json
end.to raise_error(Pundit::NotAuthorizedError)
end
end
end
end

0 comments on commit ba3fe5d

Please sign in to comment.