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:
Shivam Mishra
2024-02-06 09:04:04 +05:30
committed by GitHub
parent 9e0468cd73
commit 8acc818f68
7 changed files with 274 additions and 40 deletions

View 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

View File

@@ -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)

View File

@@ -14,4 +14,8 @@ class UserPolicy < ApplicationPolicy
def destroy?
@account_user.administrator?
end
def bulk_create?
@account_user.administrator?
end
end