Chore: Scope URLs with account_id (#601)

* Chore: Enable Users to create multiple accounts

Addresses: #402
- migrations to split roles and other attributes from users table
- make changes in code to accommodate this change

Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
This commit is contained in:
Sojan Jose
2020-03-09 23:27:10 +05:30
committed by GitHub
parent 2a6670f0da
commit 19ab0fe108
105 changed files with 480 additions and 402 deletions

View File

@@ -0,0 +1,36 @@
class Api::V1::Accounts::AccountsController < Api::BaseController
include AuthHelper
skip_before_action :verify_authenticity_token, only: [:create]
skip_before_action :authenticate_user!, :set_current_user, :check_subscription, :handle_with_exception,
only: [:create], raise: false
before_action :check_signup_enabled
rescue_from CustomExceptions::Account::InvalidEmail,
CustomExceptions::Account::UserExists,
CustomExceptions::Account::UserErrors,
with: :render_error_response
def create
@user = AccountBuilder.new(
account_name: account_params[:account_name],
email: account_params[:email]
).perform
if @user
send_auth_headers(@user)
render 'devise/auth.json', locals: { resource: @user }
else
render_error_response(CustomExceptions::Account::SignupFailed.new({}))
end
end
private
def account_params
params.permit(:account_name, :email)
end
def check_signup_enabled
raise ActionController::RoutingError, 'Not Found' if ENV.fetch('ENABLE_ACCOUNT_SIGNUP', true) == 'false'
end
end

View File

@@ -0,0 +1,28 @@
class Api::V1::Accounts::Actions::ContactMergesController < Api::BaseController
before_action :set_base_contact, only: [:create]
before_action :set_mergee_contact, only: [:create]
def create
contact_merge_action = ContactMergeAction.new(
account: current_account,
base_contact: @base_contact,
mergee_contact: @mergee_contact
)
contact_merge_action.perform
render json: @base_contact
end
private
def set_base_contact
@base_contact = contacts.find(params[:base_contact_id])
end
def set_mergee_contact
@mergee_contact = contacts.find(params[:mergee_contact_id])
end
def contacts
@contacts ||= current_account.contacts
end
end

View File

@@ -0,0 +1,69 @@
class Api::V1::Accounts::AgentsController < Api::BaseController
before_action :fetch_agent, except: [:create, :index]
before_action :check_authorization
before_action :find_user, only: [:create]
before_action :create_user, only: [:create]
before_action :save_account_user, only: [:create]
def index
@agents = agents
end
def destroy
@agent.account_user.destroy
head :ok
end
def update
@agent.update!(agent_params.except(:role))
@agent.account_user.update!(role: agent_params[:role]) if agent_params[:role]
render 'api/v1/models/user.json', locals: { resource: @agent }
end
def create
render 'api/v1/models/user.json', locals: { resource: @user }
end
private
def check_authorization
authorize(User)
end
def fetch_agent
@agent = agents.find(params[:id])
end
def find_user
@user = User.find_by(email: new_agent_params[:email])
end
def create_user
return 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,
role: new_agent_params[:role],
inviter_id: current_user.id
)
end
def agent_params
params.require(:agent).permit(:email, :name, :role)
end
def new_agent_params
time = Time.now.to_i
params.require(:agent).permit(:email, :name, :role)
.merge!(password: time, password_confirmation: time, inviter: current_user)
end
def agents
@agents ||= current_account.users
end
end

View File

@@ -0,0 +1,101 @@
class Api::V1::Accounts::CallbacksController < Api::BaseController
before_action :inbox, only: [:reauthorize_page]
def register_facebook_page
user_access_token = params[:user_access_token]
page_access_token = params[:page_access_token]
page_name = params[:page_name]
page_id = params[:page_id]
inbox_name = params[:inbox_name]
facebook_channel = current_account.facebook_pages.create!(
name: page_name, page_id: page_id, user_access_token: user_access_token,
page_access_token: page_access_token
)
set_avatar(facebook_channel, page_id)
inbox = current_account.inboxes.create!(name: inbox_name, channel: facebook_channel)
render json: inbox
end
def facebook_pages
@page_details = mark_already_existing_facebook_pages(fb_object.get_connections('me', 'accounts'))
end
# get params[:inbox_id], current_account, params[:omniauth_token]
def reauthorize_page
if @inbox&.facebook?
fb_page_id = @inbox.channel.page_id
page_details = fb_object.get_connections('me', 'accounts')
if (page_detail = (page_details || []).detect { |page| fb_page_id == page['id'] })
update_fb_page(fb_page_id, page_detail['access_token'])
return head :ok
end
end
head :unprocessable_entity
end
private
def inbox
@inbox = current_account.inboxes.find_by(id: params[:inbox_id])
end
def update_fb_page(fb_page_id, access_token)
get_fb_page(fb_page_id)&.update!(
user_access_token: @user_access_token, page_access_token: access_token
)
end
def get_fb_page(fb_page_id)
current_account.facebook_pages.find_by(page_id: fb_page_id)
end
def fb_object
@user_access_token = long_lived_token(params[:omniauth_token])
Koala::Facebook::API.new(@user_access_token)
end
def long_lived_token(omniauth_token)
koala = Koala::Facebook::OAuth.new(ENV['FB_APP_ID'], ENV['FB_APP_SECRET'])
koala.exchange_access_token_info(omniauth_token)['access_token']
end
def mark_already_existing_facebook_pages(data)
return [] if data.empty?
data.inject([]) do |result, page_detail|
page_detail[:exists] = current_account.facebook_pages.exists?(page_id: page_detail['id']) ? true : false
result << page_detail
end
end
def set_avatar(facebook_channel, page_id)
uri = get_avatar_url(page_id)
return unless uri
avatar_resource = LocalResource.new(uri)
facebook_channel.avatar.attach(io: avatar_resource.file, filename: avatar_resource.tmp_filename, content_type: avatar_resource.encoding)
end
def get_avatar_url(page_id)
begin
url = 'http://graph.facebook.com/' << page_id << '/picture?type=large'
uri = URI.parse(url)
tries = 3
begin
response = uri.open(redirect: false)
rescue OpenURI::HTTPRedirect => e
uri = e.uri # assigned from the "Location" response header
retry if (tries -= 1).positive?
raise
end
pic_url = response.base_uri.to_s
rescue StandardError => e
Rails.logger.debug "Rescued: #{e.inspect}"
pic_url = nil
end
pic_url
end
end

View File

@@ -0,0 +1,41 @@
class Api::V1::Accounts::CannedResponsesController < Api::BaseController
before_action :fetch_canned_response, only: [:update, :destroy]
def index
render json: canned_responses
end
def create
@canned_response = current_account.canned_responses.new(canned_response_params)
@canned_response.save!
render json: @canned_response
end
def update
@canned_response.update!(canned_response_params)
render json: @canned_response
end
def destroy
@canned_response.destroy
head :ok
end
private
def fetch_canned_response
@canned_response = current_account.canned_responses.find(params[:id])
end
def canned_response_params
params.require(:canned_response).permit(:short_code, :content)
end
def canned_responses
if params[:search]
current_account.canned_responses.where('short_code ILIKE ?', "#{params[:search]}%")
else
current_account.canned_responses
end
end
end

View File

@@ -0,0 +1,23 @@
class Api::V1::Accounts::Contacts::ConversationsController < Api::BaseController
def index
@conversations = current_account.conversations.includes(
:assignee, :contact, :inbox
).where(inbox_id: inbox_ids, contact_id: permitted_params[:contact_id])
end
private
def inbox_ids
if current_user.administrator?
current_account.inboxes.pluck(:id)
elsif current_user.agent?
current_user.assigned_inboxes.pluck(:id)
else
[]
end
end
def permitted_params
params.permit(:contact_id)
end
end

View File

@@ -0,0 +1,45 @@
class Api::V1::Accounts::ContactsController < Api::BaseController
protect_from_forgery with: :null_session
before_action :check_authorization
before_action :fetch_contact, only: [:show, :update]
skip_before_action :authenticate_user!, only: [:create]
skip_before_action :set_current_user, only: [:create]
skip_before_action :check_subscription, only: [:create]
skip_around_action :handle_with_exception, only: [:create]
def index
@contacts = current_account.contacts
end
def show; end
def create
@contact = Contact.new(contact_create_params)
@contact.save!
render json: @contact
end
def update
@contact.update!(contact_params)
end
private
def check_authorization
authorize(Contact)
end
def contact_params
params.require(:contact).permit(:name, :email, :phone_number)
end
def fetch_contact
@contact = current_account.contacts.find(params[:id])
end
def contact_create_params
params.require(:contact).permit(:account_id, :inbox_id).merge!(name: SecureRandom.hex)
end
end

View File

@@ -0,0 +1,11 @@
class Api::V1::Accounts::Conversations::AssignmentsController < Api::BaseController
before_action :set_conversation, only: [:create]
# assign agent to a conversation
def create
# if params[:assignee_id] is not a valid id, it will set to nil, hence unassigning the conversation
assignee = current_account.users.find_by(id: params[:assignee_id])
@conversation.update_assignee(assignee)
render json: assignee
end
end

View File

@@ -0,0 +1,13 @@
class Api::V1::Accounts::Conversations::LabelsController < Api::BaseController
before_action :set_conversation, only: [:create, :index]
def create
@conversation.update_labels(params[:labels])
@labels = @conversation.label_list
end
# all labels of the current conversation
def index
@labels = @conversation.label_list
end
end

View File

@@ -0,0 +1,18 @@
class Api::V1::Accounts::Conversations::MessagesController < Api::BaseController
before_action :set_conversation, only: [:index, :create]
def index
@messages = message_finder.perform
end
def create
mb = Messages::Outgoing::NormalBuilder.new(current_user, @conversation, params)
@message = mb.perform
end
private
def message_finder
@message_finder ||= MessageFinder.new(@conversation, params)
end
end

View File

@@ -0,0 +1,35 @@
class Api::V1::Accounts::ConversationsController < Api::BaseController
before_action :conversation, except: [:index]
def index
result = conversation_finder.perform
@conversations = result[:conversations]
@conversations_count = result[:count]
end
def show; end
def toggle_status
@status = @conversation.toggle_status
end
def update_last_seen
@conversation.agent_last_seen_at = parsed_last_seen_at
@conversation.save!
head :ok
end
private
def parsed_last_seen_at
DateTime.strptime(params[:agent_last_seen_at].to_s, '%s')
end
def conversation
@conversation ||= current_account.conversations.find_by(display_id: params[:id])
end
def conversation_finder
@conversation_finder ||= ConversationFinder.new(current_user, params)
end
end

View File

@@ -0,0 +1,55 @@
class Api::V1::Accounts::FacebookIndicatorsController < Api::BaseController
before_action :set_access_token
around_action :handle_with_exception
def mark_seen
fb_bot.deliver(payload('mark_seen'), access_token: @access_token)
head :ok
end
def typing_on
fb_bot.deliver(payload('typing_on'), access_token: @access_token)
head :ok
end
def typing_off
fb_bot.deliver(payload('typing_off'), access_token: @access_token)
head :ok
end
private
def fb_bot
::Facebook::Messenger::Bot
end
def handle_with_exception
yield
rescue Facebook::Messenger::Error => e
Rails.logger.debug "Rescued: #{e.inspect}"
true
end
def payload(action)
{
recipient: { id: contact.source_id },
sender_action: action
}
end
def inbox
@inbox ||= current_account.inboxes.find(permitted_params[:inbox_id])
end
def set_access_token
@access_token = inbox.channel.page_access_token
end
def contact
@contact ||= inbox.contact_inboxes.find_by!(contact_id: permitted_params[:contact_id])
end
def permitted_params
params.permit(:inbox_id, :contact_id)
end
end

View File

@@ -0,0 +1,45 @@
class Api::V1::Accounts::InboxMembersController < Api::BaseController
before_action :fetch_inbox, only: [:create, :show]
before_action :current_agents_ids, only: [:create]
def create
# update also done via same action
update_agents_list
head :ok
rescue StandardError => e
Rails.logger.debug "Rescued: #{e.inspect}"
render_could_not_create_error('Could not add agents to inbox')
end
def show
@agents = current_account.users.where(id: @inbox.members.pluck(:user_id))
end
private
def update_agents_list
# get all the user_ids which the inbox currently has as members.
# get the list of user_ids from params
# the missing ones are the agents which are to be deleted from the inbox
# the new ones are the agents which are to be added to the inbox
agents_to_be_added_ids.each { |user_id| @inbox.add_member(user_id) }
agents_to_be_removed_ids.each { |user_id| @inbox.remove_member(user_id) }
end
def agents_to_be_added_ids
params[:user_ids] - @current_agents_ids
end
def agents_to_be_removed_ids
@current_agents_ids - params[:user_ids]
end
def current_agents_ids
@current_agents_ids = @inbox.members.pluck(:id)
end
def fetch_inbox
@inbox = current_account.inboxes.find(params[:inbox_id])
end
end

View File

@@ -0,0 +1,31 @@
class Api::V1::Accounts::InboxesController < Api::BaseController
before_action :check_authorization
before_action :fetch_inbox, only: [:destroy, :update]
def index
@inboxes = policy_scope(current_account.inboxes)
end
def destroy
@inbox.destroy
head :ok
end
def update
@inbox.update(inbox_update_params)
end
private
def fetch_inbox
@inbox = current_account.inboxes.find(params[:id])
end
def check_authorization
authorize(Inbox)
end
def inbox_update_params
params.require(:inbox).permit(:enable_auto_assignment)
end
end

View File

@@ -0,0 +1,10 @@
class Api::V1::Accounts::LabelsController < Api::BaseController
# list all labels in account
def index
@labels = current_account.all_conversation_tags
end
def most_used
@labels = ActsAsTaggableOn::Tag.most_used(params[:count] || 10)
end
end

View File

@@ -0,0 +1,29 @@
class Api::V1::Accounts::NotificationSettingsController < Api::BaseController
before_action :set_user, :load_notification_setting
def show; end
def update
update_flags
@notification_setting.save!
render action: 'show'
end
private
def set_user
@user = current_user
end
def load_notification_setting
@notification_setting = @user.notification_settings.find_by(account_id: current_account.id)
end
def notification_setting_params
params.require(:notification_settings).permit(selected_email_flags: [])
end
def update_flags
@notification_setting.selected_email_flags = notification_setting_params[:selected_email_flags]
end
end

View File

@@ -0,0 +1,99 @@
class Api::V1::Accounts::ReportsController < Api::BaseController
include CustomExceptions::Report
include Constants::Report
around_action :report_exception
def account
builder = ReportBuilder.new(current_account, account_report_params)
data = builder.build
render json: data
end
def agent
builder = ReportBuilder.new(current_account, agent_report_params)
data = builder.build
render json: data
end
def account_summary
render json: account_summary_metrics
end
def agent_summary
render json: agent_summary_metrics
end
private
def report_exception
yield
rescue InvalidIdentity, IdentityNotFound, MetricNotFound, InvalidStartTime, InvalidEndTime => e
render_error_response(e)
end
def current_account
current_user.account
end
def account_summary_metrics
summary_metrics(ACCOUNT_METRICS, :account_summary_params, AVG_ACCOUNT_METRICS)
end
def agent_summary_metrics
summary_metrics(AGENT_METRICS, :agent_summary_params, AVG_AGENT_METRICS)
end
def summary_metrics(metrics, calc_function, avg_metrics)
metrics.each_with_object({}) do |metric, result|
data = ReportBuilder.new(current_account, send(calc_function, metric)).build
result[metric] = calculate_metric(data, metric, avg_metrics)
end
end
def calculate_metric(data, metric, avg_metrics)
sum = data.inject(0) { |val, hash| val + hash[:value].to_i }
if avg_metrics.include?(metric)
sum /= data.length unless sum.zero?
end
sum
end
def account_summary_params(metric)
{
metric: metric.to_s,
type: :account,
since: params[:since],
until: params[:until]
}
end
def agent_summary_params(metric)
{
metric: metric.to_s,
type: :agent,
since: params[:since],
until: params[:until],
id: params[:id]
}
end
def account_report_params
{
metric: params[:metric],
type: :account,
since: params[:since],
until: params[:until]
}
end
def agent_report_params
{
metric: params[:metric],
type: :agent,
id: params[:id],
since: params[:since],
until: params[:until]
}
end
end

View File

@@ -0,0 +1,13 @@
class Api::V1::Accounts::SubscriptionsController < Api::BaseController
skip_before_action :check_subscription
before_action :check_billing_enabled
def index
render json: current_account.subscription_data
end
def status
render json: current_account.subscription.summary
end
end

View File

@@ -0,0 +1,36 @@
class Api::V1::Accounts::WebhooksController < Api::BaseController
before_action :check_authorization
before_action :fetch_webhook, only: [:update, :destroy]
def index
@webhooks = current_account.webhooks
end
def create
@webhook = current_account.webhooks.new(webhook_params)
@webhook.save!
end
def update
@webhook.update!(webhook_params)
end
def destroy
@webhook.destroy
head :ok
end
private
def webhook_params
params.require(:webhook).permit(:inbox_id, :url)
end
def fetch_webhook
@webhook = current_account.webhooks.find(params[:id])
end
def check_authorization
authorize(Webhook)
end
end