Chore: Events Cleanup (#739)
* Chore: Events Cleanup - fix failing tests - add additional webhook events - clean up event logic * Fix conversation status update in action cable Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
This commit is contained in:
parent
ecccb103a0
commit
c0ce70e87b
14 changed files with 150 additions and 69 deletions
2
Gemfile
2
Gemfile
|
@ -100,7 +100,7 @@ group :development, :test do
|
|||
gem 'factory_bot_rails'
|
||||
gem 'faker'
|
||||
gem 'listen'
|
||||
gem 'mock_redis'
|
||||
gem 'mock_redis', git: 'https://github.com/sds/mock_redis', ref: '16d00789f0341a3aac35126c0ffe97a596753ff9'
|
||||
gem 'pry-rails'
|
||||
gem 'rspec-rails', '~> 4.0.0.beta2'
|
||||
gem 'rubocop', require: false
|
||||
|
|
10
Gemfile.lock
10
Gemfile.lock
|
@ -5,6 +5,13 @@ GIT
|
|||
twitty (0.1.0)
|
||||
oauth
|
||||
|
||||
GIT
|
||||
remote: https://github.com/sds/mock_redis
|
||||
revision: 16d00789f0341a3aac35126c0ffe97a596753ff9
|
||||
ref: 16d00789f0341a3aac35126c0ffe97a596753ff9
|
||||
specs:
|
||||
mock_redis (0.22.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/tzmfreedom/json_refs
|
||||
revision: e32deb073ce9aef39bdd63556bffd7fe7c2a803d
|
||||
|
@ -270,7 +277,6 @@ GEM
|
|||
mini_mime (1.0.2)
|
||||
mini_portile2 (2.4.0)
|
||||
minitest (5.14.0)
|
||||
mock_redis (0.22.0)
|
||||
msgpack (1.3.3)
|
||||
multi_json (1.14.1)
|
||||
multi_xml (0.6.0)
|
||||
|
@ -526,7 +532,7 @@ DEPENDENCIES
|
|||
letter_opener
|
||||
listen
|
||||
mini_magick
|
||||
mock_redis
|
||||
mock_redis!
|
||||
nightfury
|
||||
pg
|
||||
pry-rails
|
||||
|
|
|
@ -8,7 +8,8 @@ class ActionCableConnector extends BaseActionCableConnector {
|
|||
'message.created': this.onMessageCreated,
|
||||
'message.updated': this.onMessageUpdated,
|
||||
'conversation.created': this.onConversationCreated,
|
||||
'status_change:conversation': this.onStatusChange,
|
||||
'conversation.opened': this.onStatusChange,
|
||||
'conversation.resolved': this.onStatusChange,
|
||||
'user:logout': this.onLogout,
|
||||
'page:reload': this.onReload,
|
||||
'assignee.changed': this.onAssigneeChanged,
|
||||
|
@ -40,7 +41,7 @@ class ActionCableConnector extends BaseActionCableConnector {
|
|||
onReload = () => window.location.reload();
|
||||
|
||||
onStatusChange = data => {
|
||||
this.app.$store.dispatch('addConversation', data);
|
||||
this.app.$store.dispatch('updateConversation', data);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -149,8 +149,15 @@ const actions = {
|
|||
commit(types.default.ADD_MESSAGE, message);
|
||||
},
|
||||
|
||||
addConversation({ commit }, conversation) {
|
||||
commit(types.default.ADD_CONVERSATION, conversation);
|
||||
addConversation({ commit, state }, conversation) {
|
||||
const { currentInbox } = state;
|
||||
if (!currentInbox || Number(currentInbox) === conversation.inbox_id) {
|
||||
commit(types.default.ADD_CONVERSATION, conversation);
|
||||
}
|
||||
},
|
||||
|
||||
updateConversation({ commit }, conversation) {
|
||||
commit(types.default.UPDATE_CONVERSATION, conversation);
|
||||
},
|
||||
|
||||
toggleTyping: async ({ commit }, { status, inboxId, contactId }) => {
|
||||
|
|
|
@ -153,6 +153,24 @@ const mutations = {
|
|||
_state.allConversations.push(conversation);
|
||||
},
|
||||
|
||||
[types.default.UPDATE_CONVERSATION](_state, conversation) {
|
||||
const { allConversations } = _state;
|
||||
const currentConversationIndex = allConversations.findIndex(
|
||||
c => c.id === conversation.id
|
||||
);
|
||||
if (currentConversationIndex > -1) {
|
||||
const currentConversation = {
|
||||
...allConversations[currentConversationIndex],
|
||||
status: conversation.status,
|
||||
};
|
||||
Vue.set(allConversations, currentConversationIndex, currentConversation);
|
||||
if (_state.selectedChat.id === conversation.id) {
|
||||
_state.selectedChat.status = conversation.status;
|
||||
window.bus.$emit('scrollToMessage');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
[types.default.MARK_SEEN](_state) {
|
||||
_state.selectedChat.seen = true;
|
||||
},
|
||||
|
|
|
@ -21,6 +21,7 @@ export default {
|
|||
CLEAR_ALL_MESSAGES: 'CLEAR_ALL_MESSAGES',
|
||||
RESOLVE_CONVERSATION: 'RESOLVE_CONVERSATION',
|
||||
ADD_CONVERSATION: 'ADD_CONVERSATION',
|
||||
UPDATE_CONVERSATION: 'UPDATE_CONVERSATION',
|
||||
SEND_MESSAGE: 'SEND_MESSAGE',
|
||||
ASSIGN_AGENT: 'ASSIGN_AGENT',
|
||||
SET_CHAT_META: 'SET_CHAT_META',
|
||||
|
|
|
@ -3,21 +3,21 @@ class ActionCableListener < BaseListener
|
|||
|
||||
def conversation_created(event)
|
||||
conversation, account, timestamp = extract_conversation_and_account(event)
|
||||
send_to_administrators(account.administrators, CONVERSATION_CREATED, conversation.push_event_data)
|
||||
send_to_agents(conversation.inbox.members, CONVERSATION_CREATED, conversation.push_event_data)
|
||||
|
||||
send_to_agents(account, conversation.inbox.members, CONVERSATION_CREATED, conversation.push_event_data)
|
||||
end
|
||||
|
||||
def conversation_read(event)
|
||||
conversation, account, timestamp = extract_conversation_and_account(event)
|
||||
send_to_administrators(account.administrators, CONVERSATION_READ, conversation.push_event_data)
|
||||
send_to_agents(conversation.inbox.members, CONVERSATION_READ, conversation.push_event_data)
|
||||
|
||||
send_to_agents(account, conversation.inbox.members, CONVERSATION_READ, conversation.push_event_data)
|
||||
end
|
||||
|
||||
def message_created(event)
|
||||
message, account, timestamp = extract_message_and_account(event)
|
||||
conversation = message.conversation
|
||||
send_to_administrators(account.administrators, MESSAGE_CREATED, message.push_event_data)
|
||||
send_to_agents(conversation.inbox.members, MESSAGE_CREATED, message.push_event_data)
|
||||
|
||||
send_to_agents(account, conversation.inbox.members, MESSAGE_CREATED, message.push_event_data)
|
||||
send_to_contact(conversation.contact, MESSAGE_CREATED, message)
|
||||
end
|
||||
|
||||
|
@ -25,55 +25,58 @@ class ActionCableListener < BaseListener
|
|||
message, account, timestamp = extract_message_and_account(event)
|
||||
conversation = message.conversation
|
||||
contact = conversation.contact
|
||||
members = conversation.inbox.members.pluck(:pubsub_token)
|
||||
send_to_members(members, MESSAGE_UPDATED, message.push_event_data)
|
||||
|
||||
send_to_agents(account, conversation.inbox.members, MESSAGE_UPDATED, message.push_event_data)
|
||||
send_to_contact(contact, MESSAGE_UPDATED, message)
|
||||
end
|
||||
|
||||
def conversation_reopened(event)
|
||||
def conversation_resolved(event)
|
||||
conversation, account, timestamp = extract_conversation_and_account(event)
|
||||
send_to_administrators(account.administrators, CONVERSATION_REOPENED, conversation.push_event_data)
|
||||
send_to_agents(conversation.inbox.members, CONVERSATION_REOPENED, conversation.push_event_data)
|
||||
|
||||
send_to_agents(account, conversation.inbox.members, CONVERSATION_RESOLVED, conversation.push_event_data)
|
||||
end
|
||||
|
||||
def conversation_opened(event)
|
||||
conversation, account, timestamp = extract_conversation_and_account(event)
|
||||
|
||||
send_to_agents(account, conversation.inbox.members, CONVERSATION_OPENED, conversation.push_event_data)
|
||||
end
|
||||
|
||||
def conversation_lock_toggle(event)
|
||||
conversation, account, timestamp = extract_conversation_and_account(event)
|
||||
send_to_administrators(account.administrators, CONVERSATION_LOCK_TOGGLE, conversation.lock_event_data)
|
||||
send_to_agents(conversation.inbox.members, CONVERSATION_LOCK_TOGGLE, conversation.lock_event_data)
|
||||
|
||||
send_to_agents(account, conversation.inbox.members, CONVERSATION_LOCK_TOGGLE, conversation.lock_event_data)
|
||||
end
|
||||
|
||||
def assignee_changed(event)
|
||||
conversation, account, timestamp = extract_conversation_and_account(event)
|
||||
send_to_administrators(account.administrators, ASSIGNEE_CHANGED, conversation.push_event_data)
|
||||
send_to_agents(conversation.inbox.members, ASSIGNEE_CHANGED, conversation.push_event_data)
|
||||
|
||||
send_to_agents(account, conversation.inbox.members, ASSIGNEE_CHANGED, conversation.push_event_data)
|
||||
end
|
||||
|
||||
def contact_created(event)
|
||||
contact, account, timestamp = extract_contact_and_account(event)
|
||||
send_to_administrators(account.administrators, CONTACT_CREATED, contact.push_event_data)
|
||||
send_to_agents(account.agents, CONTACT_CREATED, contact.push_event_data)
|
||||
|
||||
send_to_agents(account, account.agents, CONTACT_CREATED, contact.push_event_data)
|
||||
end
|
||||
|
||||
def contact_updated(event)
|
||||
contact, account, timestamp = extract_contact_and_account(event)
|
||||
send_to_administrators(account.administrators, CONTACT_UPDATED, contact.push_event_data)
|
||||
send_to_agents(account.agents, CONTACT_UPDATED, contact.push_event_data)
|
||||
|
||||
send_to_agents(account, account.agents, CONTACT_UPDATED, contact.push_event_data)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def send_to_administrators(admins, event_name, data)
|
||||
admin_tokens = admins.pluck(:pubsub_token)
|
||||
return if admin_tokens.blank?
|
||||
|
||||
::ActionCableBroadcastJob.perform_later(admin_tokens, event_name, data)
|
||||
end
|
||||
|
||||
def send_to_agents(agents, event_name, data)
|
||||
def send_to_agents(account, agents, event_name, data)
|
||||
agent_tokens = agents.pluck(:pubsub_token)
|
||||
return if agent_tokens.blank?
|
||||
admin_tokens = account.administrators.pluck(:pubsub_token)
|
||||
|
||||
::ActionCableBroadcastJob.perform_later(agent_tokens, event_name, data)
|
||||
pubsub_tokens = (agent_tokens + admin_tokens).uniq
|
||||
|
||||
return if pubsub_tokens.blank?
|
||||
|
||||
::ActionCableBroadcastJob.perform_later(pubsub_tokens, event_name, data)
|
||||
end
|
||||
|
||||
def send_to_contact(contact, event_name, message)
|
||||
|
|
|
@ -1,4 +1,28 @@
|
|||
class AgentBotListener < BaseListener
|
||||
def conversation_resolved(event)
|
||||
conversation = extract_conversation_and_account(event)[0]
|
||||
inbox = conversation.inbox
|
||||
return if inbox.agent_bot_inbox.blank?
|
||||
return unless inbox.agent_bot_inbox.active?
|
||||
|
||||
agent_bot = inbox.agent_bot_inbox.agent_bot
|
||||
|
||||
payload = conversation.webhook_data.merge(event: __method__.to_s)
|
||||
AgentBotJob.perform_later(agent_bot.outgoing_url, payload)
|
||||
end
|
||||
|
||||
def conversation_opened(event)
|
||||
conversation = extract_conversation_and_account(event)[0]
|
||||
inbox = conversation.inbox
|
||||
return if inbox.agent_bot_inbox.blank?
|
||||
return unless inbox.agent_bot_inbox.active?
|
||||
|
||||
agent_bot = inbox.agent_bot_inbox.agent_bot
|
||||
|
||||
payload = conversation.webhook_data.merge(event: __method__.to_s)
|
||||
AgentBotJob.perform_later(agent_bot.outgoing_url, payload)
|
||||
end
|
||||
|
||||
def message_created(event)
|
||||
message = extract_message_and_account(event)[0]
|
||||
inbox = message.inbox
|
||||
|
|
|
@ -7,9 +7,13 @@ class ReportingListener < BaseListener
|
|||
def conversation_resolved(event)
|
||||
conversation, account, timestamp = extract_conversation_and_account(event)
|
||||
time_to_resolve = conversation.updated_at.to_i - conversation.created_at.to_i
|
||||
agent = conversation.assignee
|
||||
::Reports::UpdateAgentIdentity.new(account, agent, timestamp).update_avg_resolution_time(time_to_resolve)
|
||||
::Reports::UpdateAgentIdentity.new(account, agent, timestamp).incr_resolutions_count
|
||||
|
||||
if conversation.assignee.present?
|
||||
agent = conversation.assignee
|
||||
::Reports::UpdateAgentIdentity.new(account, agent, timestamp).update_avg_resolution_time(time_to_resolve)
|
||||
::Reports::UpdateAgentIdentity.new(account, agent, timestamp).incr_resolutions_count
|
||||
end
|
||||
|
||||
::Reports::UpdateAccountIdentity.new(account, timestamp).update_avg_resolution_time(time_to_resolve)
|
||||
::Reports::UpdateAccountIdentity.new(account, timestamp).incr_resolutions_count
|
||||
end
|
||||
|
|
|
@ -1,4 +1,18 @@
|
|||
class WebhookListener < BaseListener
|
||||
def conversation_resolved(event)
|
||||
conversation = extract_conversation_and_account(event)[0]
|
||||
inbox = conversation.inbox
|
||||
payload = conversation.webhook_data.merge(event: __method__.to_s)
|
||||
deliver_webhook_payloads(payload, inbox)
|
||||
end
|
||||
|
||||
def conversation_opened(event)
|
||||
conversation = extract_conversation_and_account(event)[0]
|
||||
inbox = conversation.inbox
|
||||
payload = conversation.webhook_data.merge(event: __method__.to_s)
|
||||
deliver_webhook_payloads(payload, inbox)
|
||||
end
|
||||
|
||||
def message_created(event)
|
||||
message = extract_message_and_account(event)[0]
|
||||
inbox = message.inbox
|
||||
|
|
|
@ -54,7 +54,7 @@ class Conversation < ApplicationRecord
|
|||
|
||||
after_update :notify_status_change, :create_activity, :send_email_notification_to_assignee
|
||||
|
||||
after_create :send_events, :run_round_robin
|
||||
after_create :notify_conversation_creation, :run_round_robin
|
||||
|
||||
acts_as_taggable_on :labels
|
||||
|
||||
|
@ -110,11 +110,7 @@ class Conversation < ApplicationRecord
|
|||
self.status = :bot if inbox.agent_bot_inbox&.active?
|
||||
end
|
||||
|
||||
def dispatch_events
|
||||
dispatcher_dispatch(CONVERSATION_RESOLVED)
|
||||
end
|
||||
|
||||
def send_events
|
||||
def notify_conversation_creation
|
||||
dispatcher_dispatch(CONVERSATION_CREATED)
|
||||
end
|
||||
|
||||
|
@ -160,7 +156,8 @@ class Conversation < ApplicationRecord
|
|||
|
||||
def notify_status_change
|
||||
{
|
||||
CONVERSATION_RESOLVED => -> { saved_change_to_status? && resolved? && assignee.present? },
|
||||
CONVERSATION_OPENED => -> { saved_change_to_status? && open? },
|
||||
CONVERSATION_RESOLVED => -> { saved_change_to_status? && resolved? },
|
||||
CONVERSATION_READ => -> { saved_change_to_user_last_seen_at? },
|
||||
CONVERSATION_LOCK_TOGGLE => -> { saved_change_to_locked? },
|
||||
ASSIGNEE_CHANGED => -> { saved_change_to_assignee_id? }
|
||||
|
|
|
@ -139,10 +139,7 @@ class Message < ApplicationRecord
|
|||
end
|
||||
|
||||
def reopen_conversation
|
||||
if incoming? && conversation.resolved?
|
||||
conversation.toggle_status
|
||||
Rails.configuration.dispatcher.dispatch(CONVERSATION_REOPENED, Time.zone.now, conversation: conversation)
|
||||
end
|
||||
conversation.open! if incoming? && conversation.resolved?
|
||||
end
|
||||
|
||||
def execute_message_template_hooks
|
||||
|
|
|
@ -1,27 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Events::Types
|
||||
CONVERSATION_CREATED = 'conversation.created'
|
||||
CONVERSATION_RESOLVED = 'conversation.resolved'
|
||||
CONVERSATION_READ = 'conversation.read'
|
||||
WEBWIDGET_TRIGGERED = 'webwidget.triggered'
|
||||
|
||||
MESSAGE_CREATED = 'message.created'
|
||||
FIRST_REPLY_CREATED = 'first.reply.created'
|
||||
MESSAGE_UPDATED = 'message.updated'
|
||||
CONVERSATION_REOPENED = 'conversation.reopened'
|
||||
CONVERSATION_LOCK_TOGGLE = 'conversation.lock_toggle'
|
||||
ASSIGNEE_CHANGED = 'assignee.changed'
|
||||
|
||||
CONTACT_CREATED = 'contact.created'
|
||||
CONTACT_UPDATED = 'contact.updated'
|
||||
|
||||
# account events
|
||||
ACCOUNT_CREATED = 'account.created'
|
||||
ACCOUNT_DESTROYED = 'account.destroyed'
|
||||
|
||||
# channel events
|
||||
WEBWIDGET_TRIGGERED = 'webwidget.triggered'
|
||||
|
||||
# conversation events
|
||||
CONVERSATION_CREATED = 'conversation.created'
|
||||
CONVERSATION_READ = 'conversation.read'
|
||||
CONVERSATION_OPENED = 'conversation.opened'
|
||||
CONVERSATION_RESOLVED = 'conversation.resolved'
|
||||
CONVERSATION_LOCK_TOGGLE = 'conversation.lock_toggle'
|
||||
ASSIGNEE_CHANGED = 'assignee.changed'
|
||||
|
||||
# message events
|
||||
MESSAGE_CREATED = 'message.created'
|
||||
FIRST_REPLY_CREATED = 'first.reply.created'
|
||||
MESSAGE_UPDATED = 'message.updated'
|
||||
|
||||
# contact events
|
||||
CONTACT_CREATED = 'contact.created'
|
||||
CONTACT_UPDATED = 'contact.updated'
|
||||
|
||||
# subscription events
|
||||
AGENT_ADDED = 'agent.added'
|
||||
AGENT_REMOVED = 'agent.removed'
|
||||
|
||||
SUBSCRIPTION_CREATED = 'subscription.created'
|
||||
SUBSCRIPTION_REACTIVATED = 'subscription.reactivated'
|
||||
SUBSCRIPTION_DEACTIVATED = 'subscription.deactivated'
|
||||
|
|
|
@ -20,9 +20,12 @@ describe ActionCableListener do
|
|||
let(:event_name) { :'message.created' }
|
||||
|
||||
it 'sends message to account admins, inbox agents and the contact' do
|
||||
expect(ActionCableBroadcastJob).to receive(:perform_later).with([admin.pubsub_token], 'message.created', message.push_event_data)
|
||||
expect(ActionCableBroadcastJob).to receive(:perform_later).with([agent.pubsub_token], 'message.created', message.push_event_data)
|
||||
expect(ActionCableBroadcastJob).to receive(:perform_later).with([conversation.contact.pubsub_token], 'message.created', message.push_event_data)
|
||||
expect(ActionCableBroadcastJob).to receive(:perform_later).with(
|
||||
[agent.pubsub_token, admin.pubsub_token], 'message.created', message.push_event_data
|
||||
)
|
||||
expect(ActionCableBroadcastJob).to receive(:perform_later).with(
|
||||
[conversation.contact.pubsub_token], 'message.created', message.push_event_data
|
||||
)
|
||||
listener.message_created(event)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue