feat: allow bulk invite create via email (#8853)
* feat: add agent builder * feat: use new agent builder * refactor: validate limit * test: agent limits * feat: allow bulk create * feat: allow bulk create * refactor: rename current_user to inviter in AgentBuilder * refactor: move limits tests to enterprise * test: send correct params * refactor: account builder returns both user and account_user * chore: Revert "refactor: account builder returns both user and account_user" This reverts commit 1419789871e8a3b8ff57af27fe53925b1486a839. * feat: return user as is * Update agent_builder.rb - minor update --------- Co-authored-by: Sojan Jose <sojan@pepalo.com>
This commit is contained in:
60
app/builders/agent_builder.rb
Normal file
60
app/builders/agent_builder.rb
Normal file
@@ -0,0 +1,60 @@
|
||||
# The AgentBuilder class is responsible for creating a new agent.
|
||||
# It initializes with necessary attributes and provides a perform method
|
||||
# to create a user and account user in a transaction.
|
||||
class AgentBuilder
|
||||
# Initializes an AgentBuilder with necessary attributes.
|
||||
# @param email [String] the email of the user.
|
||||
# @param name [String] the name of the user.
|
||||
# @param role [String] the role of the user, defaults to 'agent' if not provided.
|
||||
# @param inviter [User] the user who is inviting the agent (Current.user in most cases).
|
||||
# @param availability [String] the availability status of the user, defaults to 'offline' if not provided.
|
||||
# @param auto_offline [Boolean] the auto offline status of the user.
|
||||
pattr_initialize [:email, { name: '' }, :inviter, :account, { role: :agent }, { availability: :offline }, { auto_offline: false }]
|
||||
|
||||
# Creates a user and account user in a transaction.
|
||||
# @return [User] the created user.
|
||||
def perform
|
||||
ActiveRecord::Base.transaction do
|
||||
@user = find_or_create_user
|
||||
send_confirmation_if_required
|
||||
create_account_user
|
||||
end
|
||||
@user
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Finds a user by email or creates a new one with a temporary password.
|
||||
# @return [User] the found or created user.
|
||||
def find_or_create_user
|
||||
user = User.find_by(email: email)
|
||||
return user if user
|
||||
|
||||
temp_password = "1!aA#{SecureRandom.alphanumeric(12)}"
|
||||
User.create!(email: email, name: name, password: temp_password, password_confirmation: temp_password)
|
||||
end
|
||||
|
||||
# Sends confirmation instructions if the user is persisted and not confirmed.
|
||||
def send_confirmation_if_required
|
||||
@user.send_confirmation_instructions if user_needs_confirmation?
|
||||
end
|
||||
|
||||
# Checks if the user needs confirmation.
|
||||
# @return [Boolean] true if the user is persisted and not confirmed, false otherwise.
|
||||
def user_needs_confirmation?
|
||||
@user.persisted? && !@user.confirmed?
|
||||
end
|
||||
|
||||
# Creates an account user linking the user to the current account.
|
||||
def create_account_user
|
||||
AccountUser.create!({
|
||||
account_id: account.id,
|
||||
user_id: @user.id,
|
||||
inviter_id: inviter.id
|
||||
}.merge({
|
||||
role: role,
|
||||
availability: availability,
|
||||
auto_offline: auto_offline
|
||||
}.compact))
|
||||
end
|
||||
end
|
||||
@@ -1,16 +1,26 @@
|
||||
class Api::V1::Accounts::AgentsController < Api::V1::Accounts::BaseController
|
||||
before_action :fetch_agent, except: [:create, :index]
|
||||
before_action :fetch_agent, except: [:create, :index, :bulk_create]
|
||||
before_action :check_authorization
|
||||
before_action :find_user, only: [:create]
|
||||
before_action :validate_limit, only: [:create]
|
||||
before_action :create_user, only: [:create]
|
||||
before_action :save_account_user, only: [:create]
|
||||
before_action :validate_limit_for_bulk_create, only: [:bulk_create]
|
||||
|
||||
def index
|
||||
@agents = agents
|
||||
end
|
||||
|
||||
def create; end
|
||||
def create
|
||||
builder = AgentBuilder.new(
|
||||
email: new_agent_params['email'],
|
||||
name: new_agent_params['name'],
|
||||
role: new_agent_params['role'],
|
||||
availability: new_agent_params['availability'],
|
||||
auto_offline: new_agent_params['auto_offline'],
|
||||
inviter: current_user,
|
||||
account: Current.account
|
||||
)
|
||||
|
||||
builder.perform
|
||||
end
|
||||
|
||||
def update
|
||||
@agent.update!(agent_params.slice(:name).compact)
|
||||
@@ -23,6 +33,21 @@ class Api::V1::Accounts::AgentsController < Api::V1::Accounts::BaseController
|
||||
head :ok
|
||||
end
|
||||
|
||||
def bulk_create
|
||||
emails = params[:emails]
|
||||
|
||||
emails.each do |email|
|
||||
builder = AgentBuilder.new(
|
||||
email: email,
|
||||
name: email.split('@').first,
|
||||
inviter: current_user,
|
||||
account: Current.account
|
||||
)
|
||||
builder.perform
|
||||
end
|
||||
head :ok
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_authorization
|
||||
@@ -33,47 +58,34 @@ class Api::V1::Accounts::AgentsController < Api::V1::Accounts::BaseController
|
||||
@agent = agents.find(params[:id])
|
||||
end
|
||||
|
||||
def find_user
|
||||
@user = User.find_by(email: new_agent_params[:email])
|
||||
end
|
||||
|
||||
# TODO: move this to a builder and combine the save account user method into a builder
|
||||
# ensure the account user association is also created in a single transaction
|
||||
def create_user
|
||||
return @user.send_confirmation_instructions if @user
|
||||
|
||||
@user = User.create!(new_agent_params.slice(:email, :name, :password, :password_confirmation))
|
||||
end
|
||||
|
||||
def save_account_user
|
||||
AccountUser.create!({
|
||||
account_id: Current.account.id,
|
||||
user_id: @user.id,
|
||||
inviter_id: current_user.id
|
||||
}.merge({
|
||||
role: new_agent_params[:role],
|
||||
availability: new_agent_params[:availability],
|
||||
auto_offline: new_agent_params[:auto_offline]
|
||||
}.compact))
|
||||
end
|
||||
|
||||
def agent_params
|
||||
params.require(:agent).permit(:name, :email, :name, :role, :availability, :auto_offline)
|
||||
end
|
||||
|
||||
def new_agent_params
|
||||
# intial string ensures the password requirements are met
|
||||
temp_password = "1!aA#{SecureRandom.alphanumeric(12)}"
|
||||
params.require(:agent).permit(:email, :name, :role, :availability, :auto_offline)
|
||||
.merge!(password: temp_password, password_confirmation: temp_password, inviter: current_user)
|
||||
end
|
||||
|
||||
def agents
|
||||
@agents ||= Current.account.users.order_by_full_name.includes(:account_users, { avatar_attachment: [:blob] })
|
||||
end
|
||||
|
||||
def validate_limit_for_bulk_create
|
||||
limit_available = params[:emails].count <= available_agent_count
|
||||
|
||||
render_payment_required('Account limit exceeded. Please purchase more licenses') unless limit_available
|
||||
end
|
||||
|
||||
def validate_limit
|
||||
render_payment_required('Account limit exceeded. Please purchase more licenses') if agents.count >= Current.account.usage_limits[:agents]
|
||||
render_payment_required('Account limit exceeded. Please purchase more licenses') unless can_add_agent?
|
||||
end
|
||||
|
||||
def available_agent_count
|
||||
Current.account.usage_limits[:agents] - agents.count
|
||||
end
|
||||
|
||||
def can_add_agent?
|
||||
available_agent_count.positive?
|
||||
end
|
||||
|
||||
def delete_user_record(agent)
|
||||
|
||||
@@ -14,4 +14,8 @@ class UserPolicy < ApplicationPolicy
|
||||
def destroy?
|
||||
@account_user.administrator?
|
||||
end
|
||||
|
||||
def bulk_create?
|
||||
@account_user.administrator?
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user