chore: API improvements (#3637)
- Unique validations for Inbox members and Team member objects - Move notification processing to Async
This commit is contained in:
parent
262474166f
commit
009abc1948
13 changed files with 48 additions and 31 deletions
|
@ -1,11 +1,11 @@
|
||||||
class Api::V1::Accounts::InboxMembersController < Api::V1::Accounts::BaseController
|
class Api::V1::Accounts::InboxMembersController < Api::V1::Accounts::BaseController
|
||||||
before_action :fetch_inbox
|
before_action :fetch_inbox
|
||||||
before_action :current_agents_ids, only: [:update]
|
before_action :current_agents_ids, only: [:create, :update]
|
||||||
|
|
||||||
def create
|
def create
|
||||||
authorize @inbox, :create?
|
authorize @inbox, :create?
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
params[:user_ids].map { |user_id| @inbox.add_member(user_id) }
|
agents_to_be_added_ids.map { |user_id| @inbox.add_member(user_id) }
|
||||||
end
|
end
|
||||||
fetch_updated_agents
|
fetch_updated_agents
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ class Api::V1::Accounts::TeamMembersController < Api::V1::Accounts::BaseControll
|
||||||
|
|
||||||
def create
|
def create
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
@team_members = params[:user_ids].map { |user_id| @team.add_member(user_id) }
|
@team_members = members_to_be_added_ids.map { |user_id| @team.add_member(user_id) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ class AsyncDispatcher < BaseDispatcher
|
||||||
EventListener.instance,
|
EventListener.instance,
|
||||||
HookListener.instance,
|
HookListener.instance,
|
||||||
InstallationWebhookListener.instance,
|
InstallationWebhookListener.instance,
|
||||||
|
NotificationListener.instance,
|
||||||
WebhookListener.instance
|
WebhookListener.instance
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,6 @@ class SyncDispatcher < BaseDispatcher
|
||||||
end
|
end
|
||||||
|
|
||||||
def listeners
|
def listeners
|
||||||
[ActionCableListener.instance, AgentBotListener.instance, NotificationListener.instance]
|
[ActionCableListener.instance, AgentBotListener.instance]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@ class Inboxes::FetchImapEmailInboxesJob < ApplicationJob
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
Inbox.where(channel_type: 'Channel::Email').all.each do |inbox|
|
Inbox.where(channel_type: 'Channel::Email').all.each do |inbox|
|
||||||
Inboxes::FetchImapEmailsJob.perform_later(inbox.channel) if inbox.channel.imap_enabled
|
::Inboxes::FetchImapEmailsJob.perform_later(inbox.channel) if inbox.channel.imap_enabled
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,7 +24,7 @@ module ActivityMessageHandler
|
||||||
I18n.t('conversations.activity.status.auto_resolved', duration: auto_resolve_duration)
|
I18n.t('conversations.activity.status.auto_resolved', duration: auto_resolve_duration)
|
||||||
end
|
end
|
||||||
|
|
||||||
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
::Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_label_added(user_name, labels = [])
|
def create_label_added(user_name, labels = [])
|
||||||
|
@ -33,7 +33,7 @@ module ActivityMessageHandler
|
||||||
params = { user_name: user_name, labels: labels.join(', ') }
|
params = { user_name: user_name, labels: labels.join(', ') }
|
||||||
content = I18n.t('conversations.activity.labels.added', **params)
|
content = I18n.t('conversations.activity.labels.added', **params)
|
||||||
|
|
||||||
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
::Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_label_removed(user_name, labels = [])
|
def create_label_removed(user_name, labels = [])
|
||||||
|
@ -42,7 +42,7 @@ module ActivityMessageHandler
|
||||||
params = { user_name: user_name, labels: labels.join(', ') }
|
params = { user_name: user_name, labels: labels.join(', ') }
|
||||||
content = I18n.t('conversations.activity.labels.removed', **params)
|
content = I18n.t('conversations.activity.labels.removed', **params)
|
||||||
|
|
||||||
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
::Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_muted_message
|
def create_muted_message
|
||||||
|
@ -51,7 +51,7 @@ module ActivityMessageHandler
|
||||||
params = { user_name: Current.user.name }
|
params = { user_name: Current.user.name }
|
||||||
content = I18n.t('conversations.activity.muted', **params)
|
content = I18n.t('conversations.activity.muted', **params)
|
||||||
|
|
||||||
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
::Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_unmuted_message
|
def create_unmuted_message
|
||||||
|
@ -60,7 +60,7 @@ module ActivityMessageHandler
|
||||||
params = { user_name: Current.user.name }
|
params = { user_name: Current.user.name }
|
||||||
content = I18n.t('conversations.activity.unmuted', **params)
|
content = I18n.t('conversations.activity.unmuted', **params)
|
||||||
|
|
||||||
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
::Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_team_change_activity_key
|
def generate_team_change_activity_key
|
||||||
|
@ -82,7 +82,7 @@ module ActivityMessageHandler
|
||||||
params[:team_name] = generate_team_name_for_activity if key == 'removed'
|
params[:team_name] = generate_team_name_for_activity if key == 'removed'
|
||||||
content = I18n.t("conversations.activity.team.#{key}", **params)
|
content = I18n.t("conversations.activity.team.#{key}", **params)
|
||||||
|
|
||||||
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
::Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_assignee_change_activity_content(user_name)
|
def generate_assignee_change_activity_content(user_name)
|
||||||
|
@ -96,6 +96,6 @@ module ActivityMessageHandler
|
||||||
return unless user_name
|
return unless user_name
|
||||||
|
|
||||||
content = generate_assignee_change_activity_content(user_name)
|
content = generate_assignee_change_activity_content(user_name)
|
||||||
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
::Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,11 +11,13 @@
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
# index_inbox_members_on_inbox_id (inbox_id)
|
# index_inbox_members_on_inbox_id (inbox_id)
|
||||||
|
# index_inbox_members_on_inbox_id_and_user_id (inbox_id,user_id) UNIQUE
|
||||||
#
|
#
|
||||||
|
|
||||||
class InboxMember < ApplicationRecord
|
class InboxMember < ApplicationRecord
|
||||||
validates :inbox_id, presence: true
|
validates :inbox_id, presence: true
|
||||||
validates :user_id, presence: true
|
validates :user_id, presence: true
|
||||||
|
validates :user_id, uniqueness: { scope: :inbox_id }
|
||||||
|
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
belongs_to :inbox
|
belongs_to :inbox
|
||||||
|
|
|
@ -22,4 +22,5 @@
|
||||||
class TeamMember < ApplicationRecord
|
class TeamMember < ApplicationRecord
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
belongs_to :team
|
belongs_to :team
|
||||||
|
validates :user_id, uniqueness: { scope: :team_id }
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# ref: https://dev.to/nodefiend/rails-migration-adding-a-unique-index-and-deleting-duplicates-5cde
|
||||||
|
|
||||||
|
class AddUniqueIndexOnInboxMembers < ActiveRecord::Migration[6.1]
|
||||||
|
def up
|
||||||
|
# partioning the duplicate records and then removing where more than one row is found
|
||||||
|
ActiveRecord::Base.connection.execute('
|
||||||
|
DELETE FROM inbox_members WHERE id IN (SELECT id from (
|
||||||
|
SELECT id, user_id, inbox_id, ROW_NUMBER() OVER w AS rnum FROM inbox_members WINDOW w AS (
|
||||||
|
PARTITION BY inbox_id, user_id ORDER BY id
|
||||||
|
)
|
||||||
|
) t WHERE t.rnum > 1)
|
||||||
|
')
|
||||||
|
add_index :inbox_members, [:inbox_id, :user_id], unique: true
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_index :inbox_members, [:inbox_id, :user_id]
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2021_12_19_031453) do
|
ActiveRecord::Schema.define(version: 2021_12_21_125545) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "pg_stat_statements"
|
enable_extension "pg_stat_statements"
|
||||||
|
@ -429,6 +429,7 @@ ActiveRecord::Schema.define(version: 2021_12_19_031453) do
|
||||||
t.integer "inbox_id", null: false
|
t.integer "inbox_id", null: false
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["inbox_id", "user_id"], name: "index_inbox_members_on_inbox_id_and_user_id", unique: true
|
||||||
t.index ["inbox_id"], name: "index_inbox_members_on_inbox_id"
|
t.index ["inbox_id"], name: "index_inbox_members_on_inbox_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ RSpec.describe 'Inbox Member API', type: :request do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'add inbox members' do
|
it 'add inbox members' do
|
||||||
params = { inbox_id: inbox.id, user_ids: [agent_to_add.id] }
|
params = { inbox_id: inbox.id, user_ids: [old_agent.id, agent_to_add.id] }
|
||||||
|
|
||||||
post "/api/v1/accounts/#{account.id}/inbox_members",
|
post "/api/v1/accounts/#{account.id}/inbox_members",
|
||||||
headers: administrator.create_new_auth_token,
|
headers: administrator.create_new_auth_token,
|
||||||
|
|
|
@ -55,6 +55,8 @@ RSpec.describe 'Team Members API', type: :request do
|
||||||
it 'add a new team members when its administrator' do
|
it 'add a new team members when its administrator' do
|
||||||
user_ids = (1..5).map { create(:user, account: account, role: :agent).id }
|
user_ids = (1..5).map { create(:user, account: account, role: :agent).id }
|
||||||
params = { user_ids: user_ids }
|
params = { user_ids: user_ids }
|
||||||
|
# have a team member added already
|
||||||
|
create(:team_member, team: team, user: User.find(user_ids.first))
|
||||||
|
|
||||||
post "/api/v1/accounts/#{account.id}/teams/#{team.id}/team_members",
|
post "/api/v1/accounts/#{account.id}/teams/#{team.id}/team_members",
|
||||||
params: params,
|
params: params,
|
||||||
|
@ -63,7 +65,7 @@ RSpec.describe 'Team Members API', type: :request do
|
||||||
|
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
json_response = JSON.parse(response.body)
|
json_response = JSON.parse(response.body)
|
||||||
expect(json_response.count).to eq(user_ids.count)
|
expect(json_response.count).to eq(user_ids.count - 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -93,15 +95,16 @@ RSpec.describe 'Team Members API', type: :request do
|
||||||
|
|
||||||
it 'destroys the team members when its administrator' do
|
it 'destroys the team members when its administrator' do
|
||||||
user_ids = (1..5).map { create(:user, account: account, role: :agent).id }
|
user_ids = (1..5).map { create(:user, account: account, role: :agent).id }
|
||||||
params = { user_ids: user_ids }
|
user_ids.each { |id| create(:team_member, team: team, user: User.find(id)) }
|
||||||
|
params = { user_ids: user_ids.first(3) }
|
||||||
|
|
||||||
delete "/api/v1/accounts/#{account.id}/teams/#{team.id}",
|
delete "/api/v1/accounts/#{account.id}/teams/#{team.id}/team_members",
|
||||||
params: params,
|
params: params,
|
||||||
headers: administrator.create_new_auth_token,
|
headers: administrator.create_new_auth_token,
|
||||||
as: :json
|
as: :json
|
||||||
|
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
expect(team.team_members.count).to eq(0)
|
expect(team.team_members.count).to eq(2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -78,19 +78,9 @@ shared_examples_for 'assignment_handler' do
|
||||||
expect(conversation.reload.assignee).to eq(agent)
|
expect(conversation.reload.assignee).to eq(agent)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a new notification for the agent' do
|
it 'dispaches assignee changed event' do
|
||||||
|
expect(EventDispatcherJob).to(have_been_enqueued.at_least(:once).with('assignee.changed', anything, anything))
|
||||||
expect(update_assignee).to eq(true)
|
expect(update_assignee).to eq(true)
|
||||||
expect(agent.notifications.count).to eq(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not create assignment notification if notification setting is turned off' do
|
|
||||||
notification_setting = agent.notification_settings.first
|
|
||||||
notification_setting.unselect_all_email_flags
|
|
||||||
notification_setting.unselect_all_push_flags
|
|
||||||
notification_setting.save!
|
|
||||||
|
|
||||||
expect(update_assignee).to eq(true)
|
|
||||||
expect(agent.notifications.count).to eq(0)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when agent is current user' do
|
context 'when agent is current user' do
|
||||||
|
|
Loading…
Reference in a new issue