chore: Fix conversation status in webhooks (#3364)

- fix the wrong conversation status being sent in webhooks
- additional information in websocket events
- refactor activity messaging code
- move activity message generation to background job to stop the callback loop
This commit is contained in:
Sojan Jose 2021-11-12 16:17:59 +05:30 committed by GitHub
parent b119d9e729
commit d78cb67a2a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 219 additions and 136 deletions

View file

@ -0,0 +1,7 @@
class Conversations::ActivityMessageJob < ApplicationJob
queue_as :default
def perform(conversation, message_params)
conversation.messages.create(message_params)
end
end

View file

@ -143,6 +143,11 @@ class ActionCableListener < BaseListener
def broadcast(account, tokens, event_name, data) def broadcast(account, tokens, event_name, data)
return if tokens.blank? return if tokens.blank?
::ActionCableBroadcastJob.perform_later(tokens.uniq, event_name, data.merge(account_id: account.id)) payload = data.merge(account_id: account.id)
# So the frondend knows who performed the action.
# Useful in cases like conversation assignment for generating a notification with assigner name.
payload[:performer] = Current.user&.push_event_data if Current.user.present?
::ActionCableBroadcastJob.perform_later(tokens.uniq, event_name, payload)
end end
end end

View file

@ -0,0 +1,102 @@
module ActivityMessageHandler
extend ActiveSupport::Concern
private
def create_activity
user_name = Current.user.name if Current.user.present?
status_change_activity(user_name) if saved_change_to_status?
create_label_change(user_name) if saved_change_to_label_list?
end
def status_change_activity(user_name)
create_status_change_message(user_name)
queue_conversation_auto_resolution_job if open?
end
def activity_message_params(content)
{ account_id: account_id, inbox_id: inbox_id, message_type: :activity, content: content }
end
def create_status_change_message(user_name)
content = if user_name
I18n.t("conversations.activity.status.#{status}", user_name: user_name)
elsif resolved?
I18n.t('conversations.activity.status.auto_resolved', duration: auto_resolve_duration)
end
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
end
def create_label_added(user_name, labels = [])
return unless labels.size.positive?
params = { user_name: user_name, labels: labels.join(', ') }
content = I18n.t('conversations.activity.labels.added', **params)
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
end
def create_label_removed(user_name, labels = [])
return unless labels.size.positive?
params = { user_name: user_name, labels: labels.join(', ') }
content = I18n.t('conversations.activity.labels.removed', **params)
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
end
def create_muted_message
return unless Current.user
params = { user_name: Current.user.name }
content = I18n.t('conversations.activity.muted', **params)
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
end
def create_unmuted_message
return unless Current.user
params = { user_name: Current.user.name }
content = I18n.t('conversations.activity.unmuted', **params)
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
end
def generate_team_change_activity_key
key = team_id ? 'assigned' : 'removed'
key += '_with_assignee' if key == 'assigned' && saved_change_to_assignee_id? && assignee
key
end
def generate_team_name_for_activity
previous_team_id = previous_changes[:team_id][0]
Team.find_by(id: previous_team_id)&.name if previous_team_id.present?
end
def create_team_change_activity(user_name)
return unless user_name
key = generate_team_change_activity_key
params = { assignee_name: assignee&.name, team_name: team&.name, user_name: user_name }
params[:team_name] = generate_team_name_for_activity if key == 'removed'
content = I18n.t("conversations.activity.team.#{key}", **params)
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
end
def generate_assignee_change_activity_content(user_name)
params = { assignee_name: assignee&.name, user_name: user_name }.compact
key = assignee_id ? 'assigned' : 'removed'
key = 'self_assigned' if self_assign? assignee_id
I18n.t("conversations.activity.assignee.#{key}", **params)
end
def create_assignee_change_activity(user_name)
return unless user_name
content = generate_assignee_change_activity_content(user_name)
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
end
end

View file

@ -43,35 +43,4 @@ module AssignmentHandler
create_assignee_change_activity(user_name) create_assignee_change_activity(user_name)
end end
end end
def generate_team_change_activity_key
key = team_id ? 'assigned' : 'removed'
key += '_with_assignee' if key == 'assigned' && saved_change_to_assignee_id? && assignee
key
end
def create_team_change_activity(user_name)
return unless user_name
key = generate_team_change_activity_key
params = { assignee_name: assignee&.name, team_name: team&.name, user_name: user_name }
if key == 'removed'
previous_team_id = previous_changes[:team_id][0]
params[:team_name] = Team.find_by(id: previous_team_id)&.name if previous_team_id.present?
end
content = I18n.t("conversations.activity.team.#{key}", **params)
messages.create(activity_message_params(content))
end
def create_assignee_change_activity(user_name)
return unless user_name
params = { assignee_name: assignee&.name, user_name: user_name }.compact
key = assignee_id ? 'assigned' : 'removed'
key = 'self_assigned' if self_assign? assignee_id
content = I18n.t("conversations.activity.assignee.#{key}", **params)
messages.create(activity_message_params(content))
end
end end

View file

@ -45,6 +45,7 @@ class Conversation < ApplicationRecord
include Labelable include Labelable
include AssignmentHandler include AssignmentHandler
include RoundRobinHandler include RoundRobinHandler
include ActivityMessageHandler
validates :account_id, presence: true validates :account_id, presence: true
validates :inbox_id, presence: true validates :inbox_id, presence: true
@ -72,9 +73,7 @@ class Conversation < ApplicationRecord
before_save :ensure_snooze_until_reset before_save :ensure_snooze_until_reset
before_create :mark_conversation_pending_if_bot before_create :mark_conversation_pending_if_bot
# wanted to change this to after_update commit. But it ended up creating a loop after_update_commit :execute_after_update_commit_callbacks
# reinvestigate in future and identity the implications
after_update :notify_status_change, :create_activity
after_create_commit :notify_conversation_creation, :queue_conversation_auto_resolution_job after_create_commit :notify_conversation_creation, :queue_conversation_auto_resolution_job
after_commit :set_display_id, unless: :display_id? after_commit :set_display_id, unless: :display_id?
@ -150,6 +149,11 @@ class Conversation < ApplicationRecord
private private
def execute_after_update_commit_callbacks
notify_status_change
create_activity
end
def ensure_snooze_until_reset def ensure_snooze_until_reset
self.snoozed_until = nil unless snoozed? self.snoozed_until = nil unless snoozed?
end end
@ -168,6 +172,8 @@ class Conversation < ApplicationRecord
end end
def queue_conversation_auto_resolution_job def queue_conversation_auto_resolution_job
# FIXME: Move this to one cronjob that iterates over accounts and enqueue resolution jobs
# Similar to how we handle campaigns
return unless auto_resolve_duration return unless auto_resolve_duration
AutoResolveConversationsJob.set(wait_until: (last_activity_at || created_at) + auto_resolve_duration.days).perform_later(id) AutoResolveConversationsJob.set(wait_until: (last_activity_at || created_at) + auto_resolve_duration.days).perform_later(id)
@ -181,21 +187,6 @@ class Conversation < ApplicationRecord
reload reload
end end
def create_activity
user_name = Current.user.name if Current.user.present?
status_change_activity(user_name) if saved_change_to_status?
create_label_change(user_name) if saved_change_to_label_list?
end
def status_change_activity(user_name)
create_status_change_message(user_name)
queue_conversation_auto_resolution_job if open?
end
def activity_message_params(content)
{ account_id: account_id, inbox_id: inbox_id, message_type: :activity, content: content }
end
def notify_status_change def notify_status_change
{ {
CONVERSATION_OPENED => -> { saved_change_to_status? && open? }, CONVERSATION_OPENED => -> { saved_change_to_status? && open? },
@ -218,16 +209,6 @@ class Conversation < ApplicationRecord
return true if previous_changes.key?(:id) || saved_change_to_status? return true if previous_changes.key?(:id) || saved_change_to_status?
end end
def create_status_change_message(user_name)
content = if user_name
I18n.t("conversations.activity.status.#{status}", user_name: user_name)
elsif resolved?
I18n.t('conversations.activity.status.auto_resolved', duration: auto_resolve_duration)
end
messages.create(activity_message_params(content)) if content
end
def create_label_change(user_name) def create_label_change(user_name)
return unless user_name return unless user_name
@ -238,42 +219,6 @@ class Conversation < ApplicationRecord
create_label_removed(user_name, previous_labels - current_labels) create_label_removed(user_name, previous_labels - current_labels)
end end
def create_label_added(user_name, labels = [])
return unless labels.size.positive?
params = { user_name: user_name, labels: labels.join(', ') }
content = I18n.t('conversations.activity.labels.added', **params)
messages.create(activity_message_params(content))
end
def create_label_removed(user_name, labels = [])
return unless labels.size.positive?
params = { user_name: user_name, labels: labels.join(', ') }
content = I18n.t('conversations.activity.labels.removed', **params)
messages.create(activity_message_params(content))
end
def create_muted_message
return unless Current.user
params = { user_name: Current.user.name }
content = I18n.t('conversations.activity.muted', **params)
messages.create(activity_message_params(content))
end
def create_unmuted_message
return unless Current.user
params = { user_name: Current.user.name }
content = I18n.t('conversations.activity.unmuted', **params)
messages.create(activity_message_params(content))
end
def mute_key def mute_key
format(Redis::RedisKeys::CONVERSATION_MUTE_KEY, id: id) format(Redis::RedisKeys::CONVERSATION_MUTE_KEY, id: id)
end end

View file

@ -97,7 +97,8 @@ class Message < ApplicationRecord
data = attributes.merge( data = attributes.merge(
created_at: created_at.to_i, created_at: created_at.to_i,
message_type: message_type_before_type_cast, message_type: message_type_before_type_cast,
conversation_id: conversation.display_id conversation_id: conversation.display_id,
conversation: { assignee_id: conversation.assignee_id }
) )
data.merge!(echo_id: echo_id) if echo_id.present? data.merge!(echo_id: echo_id) if echo_id.present?
data.merge!(attachments: attachments.map(&:push_event_data)) if attachments.present? data.merge!(attachments: attachments.map(&:push_event_data)) if attachments.present?

View file

@ -64,7 +64,10 @@ RSpec.describe 'Conversation Assignment API', type: :request do
expect(response).to have_http_status(:success) expect(response).to have_http_status(:success)
expect(conversation.reload.assignee).to eq(nil) expect(conversation.reload.assignee).to eq(nil)
expect(conversation.messages.last.content).to eq("Conversation unassigned by #{agent.name}") expect(Conversations::ActivityMessageJob)
.to(have_been_enqueued.at_least(:once)
.with(conversation, { account_id: conversation.account_id, inbox_id: conversation.inbox_id, message_type: :activity,
content: "Conversation unassigned by #{agent.name}" }))
end end
end end

View file

@ -22,9 +22,13 @@ shared_examples_for 'assignment_handler' do
it 'creates team assigned and unassigned message activity' do it 'creates team assigned and unassigned message activity' do
expect(conversation.update(team: team)).to eq true expect(conversation.update(team: team)).to eq true
expect(conversation.messages.pluck(:content)).to include("Assigned to #{team.name} by #{agent.name}")
expect(conversation.update(team: nil)).to eq true expect(conversation.update(team: nil)).to eq true
expect(conversation.messages.pluck(:content)).to include("Unassigned from #{team.name} by #{agent.name}") expect(Conversations::ActivityMessageJob).to(have_been_enqueued.at_least(:once)
.with(conversation, { account_id: conversation.account_id, inbox_id: conversation.inbox_id, message_type: :activity,
content: "Assigned to #{team.name} by #{agent.name}" }))
expect(Conversations::ActivityMessageJob).to(have_been_enqueued.at_least(:once)
.with(conversation, { account_id: conversation.account_id, inbox_id: conversation.inbox_id, message_type: :activity,
content: "Unassigned from #{team.name} by #{agent.name}" }))
end end
it 'changes assignee to nil if they doesnt belong to the team and allow_auto_assign is false' do it 'changes assignee to nil if they doesnt belong to the team and allow_auto_assign is false' do
@ -41,7 +45,9 @@ shared_examples_for 'assignment_handler' do
conversation.update(team: team) conversation.update(team: team)
expect(conversation.reload.assignee).to eq agent expect(conversation.reload.assignee).to eq agent
expect(conversation.messages.pluck(:content)).to include("Assigned to #{conversation.assignee.name} via #{team.name} by #{agent.name}") expect(Conversations::ActivityMessageJob).to(have_been_enqueued.at_least(:once)
.with(conversation, { account_id: conversation.account_id, inbox_id: conversation.inbox_id, message_type: :activity,
content: "Assigned to #{conversation.assignee.name} via #{team.name} by #{agent.name}" }))
end end
it 'wont change assignee if he is already a team member' do it 'wont change assignee if he is already a team member' do
@ -94,7 +100,9 @@ shared_examples_for 'assignment_handler' do
it 'creates self-assigned message activity' do it 'creates self-assigned message activity' do
expect(update_assignee).to eq(true) expect(update_assignee).to eq(true)
expect(conversation.messages.pluck(:content)).to include("#{agent.name} self-assigned this conversation") expect(Conversations::ActivityMessageJob).to(have_been_enqueued.at_least(:once)
.with(conversation, { account_id: conversation.account_id, inbox_id: conversation.inbox_id,
message_type: :activity, content: "#{agent.name} self-assigned this conversation" }))
end end
end end
end end

View file

@ -72,36 +72,31 @@ RSpec.describe Conversation, type: :model do
end end
describe '.after_update' do describe '.after_update' do
let(:account) { create(:account) } let!(:account) { create(:account) }
let(:conversation) do let!(:old_assignee) do
create(:conversation, status: 'open', account: account, assignee: old_assignee)
end
let(:old_assignee) do
create(:user, email: 'agent1@example.com', account: account, role: :agent) create(:user, email: 'agent1@example.com', account: account, role: :agent)
end end
let(:new_assignee) do let(:new_assignee) do
create(:user, email: 'agent2@example.com', account: account, role: :agent) create(:user, email: 'agent2@example.com', account: account, role: :agent)
end end
let!(:conversation) do
create(:conversation, status: 'open', account: account, assignee: old_assignee)
end
let(:assignment_mailer) { double(deliver: true) } let(:assignment_mailer) { double(deliver: true) }
let(:label) { create(:label, account: account) } let(:label) { create(:label, account: account) }
before do before do
conversation
new_assignee
allow(Rails.configuration.dispatcher).to receive(:dispatch) allow(Rails.configuration.dispatcher).to receive(:dispatch)
Current.user = old_assignee Current.user = old_assignee
end
it 'runs after_update callbacks' do
conversation.update( conversation.update(
status: :resolved, status: :resolved,
contact_last_seen_at: Time.now, contact_last_seen_at: Time.now,
assignee: new_assignee, assignee: new_assignee,
label_list: [label.title] label_list: [label.title]
) )
end
it 'runs after_update callbacks' do
# notify_status_change
expect(Rails.configuration.dispatcher).to have_received(:dispatch) expect(Rails.configuration.dispatcher).to have_received(:dispatch)
.with(described_class::CONVERSATION_RESOLVED, kind_of(Time), conversation: conversation) .with(described_class::CONVERSATION_RESOLVED, kind_of(Time), conversation: conversation)
expect(Rails.configuration.dispatcher).to have_received(:dispatch) expect(Rails.configuration.dispatcher).to have_received(:dispatch)
@ -111,19 +106,37 @@ RSpec.describe Conversation, type: :model do
end end
it 'creates conversation activities' do it 'creates conversation activities' do
# create_activity conversation.update(
expect(conversation.messages.pluck(:content)).to include("Conversation was marked resolved by #{old_assignee.name}") status: :resolved,
expect(conversation.messages.pluck(:content)).to include("Assigned to #{new_assignee.name} by #{old_assignee.name}") contact_last_seen_at: Time.now,
expect(conversation.messages.pluck(:content)).to include("#{old_assignee.name} added #{label.title}") assignee: new_assignee,
label_list: [label.title]
)
expect(Conversations::ActivityMessageJob)
.to(have_been_enqueued.at_least(:once)
.with(conversation, { account_id: conversation.account_id, inbox_id: conversation.inbox_id, message_type: :activity,
content: "#{old_assignee.name} added #{label.title}" }))
expect(Conversations::ActivityMessageJob)
.to(have_been_enqueued.at_least(:once)
.with(conversation, { account_id: conversation.account_id, inbox_id: conversation.inbox_id, message_type: :activity,
content: "Conversation was marked resolved by #{old_assignee.name}" }))
expect(Conversations::ActivityMessageJob)
.to(have_been_enqueued.at_least(:once)
.with(conversation, { account_id: conversation.account_id, inbox_id: conversation.inbox_id, message_type: :activity,
content: "Assigned to #{new_assignee.name} by #{old_assignee.name}" }))
end end
it 'adds a message for system auto resolution if marked resolved by system' do it 'adds a message for system auto resolution if marked resolved by system' do
account.update(auto_resolve_duration: 40) account.update(auto_resolve_duration: 40)
conversation2 = create(:conversation, status: 'open', account: account, assignee: old_assignee) conversation2 = create(:conversation, status: 'open', account: account, assignee: old_assignee)
Current.user = nil Current.user = nil
conversation2.update(status: :resolved)
system_resolved_message = "Conversation was marked resolved by system due to #{account.auto_resolve_duration} days of inactivity" system_resolved_message = "Conversation was marked resolved by system due to #{account.auto_resolve_duration} days of inactivity"
expect(conversation2.messages.pluck(:content)).to include(system_resolved_message) expect { conversation2.update(status: :resolved) }
.to have_enqueued_job(Conversations::ActivityMessageJob)
.with(conversation2, { account_id: conversation2.account_id, inbox_id: conversation2.inbox_id, message_type: :activity,
content: system_resolved_message })
end end
it 'does not trigger AutoResolutionJob if conversation reopened and account does not have auto resolve duration' do it 'does not trigger AutoResolutionJob if conversation reopened and account does not have auto resolve duration' do
@ -133,8 +146,9 @@ RSpec.describe Conversation, type: :model do
it 'does trigger AutoResolutionJob if conversation reopened and account has auto resolve duration' do it 'does trigger AutoResolutionJob if conversation reopened and account has auto resolve duration' do
account.update(auto_resolve_duration: 40) account.update(auto_resolve_duration: 40)
expect { conversation.reload.update(status: :open) } conversation.resolved!
.to have_enqueued_job(AutoResolveConversationsJob).with(conversation.id) conversation.reload.update(status: :open)
expect(AutoResolveConversationsJob).to have_been_enqueued.with(conversation.id)
end end
end end
@ -161,22 +175,35 @@ RSpec.describe Conversation, type: :model do
it 'adds one label to conversation' do it 'adds one label to conversation' do
labels = [first_label].map(&:title) labels = [first_label].map(&:title)
expect(conversation.update_labels(labels)).to eq(true)
expect { conversation.update_labels(labels) }
.to have_enqueued_job(Conversations::ActivityMessageJob)
.with(conversation, { account_id: conversation.account_id, inbox_id: conversation.inbox_id, message_type: :activity,
content: "#{agent.name} added #{labels.join(', ')}" })
expect(conversation.label_list).to match_array(labels) expect(conversation.label_list).to match_array(labels)
expect(conversation.messages.pluck(:content)).to include("#{agent.name} added #{labels.join(', ')}")
end end
it 'adds and removes previously added labels' do it 'adds and removes previously added labels' do
labels = [first_label, fourth_label].map(&:title) labels = [first_label, fourth_label].map(&:title)
expect(conversation.update_labels(labels)).to eq(true) expect { conversation.update_labels(labels) }
.to have_enqueued_job(Conversations::ActivityMessageJob)
.with(conversation, { account_id: conversation.account_id, inbox_id: conversation.inbox_id, message_type: :activity,
content: "#{agent.name} added #{labels.join(', ')}" })
expect(conversation.label_list).to match_array(labels) expect(conversation.label_list).to match_array(labels)
expect(conversation.messages.pluck(:content)).to include("#{agent.name} added #{labels.join(', ')}")
updated_labels = [second_label, third_label].map(&:title) updated_labels = [second_label, third_label].map(&:title)
expect(conversation.update_labels(updated_labels)).to eq(true) expect(conversation.update_labels(updated_labels)).to eq(true)
expect(conversation.label_list).to match_array(updated_labels) expect(conversation.label_list).to match_array(updated_labels)
expect(conversation.messages.pluck(:content)).to include("#{agent.name} added #{updated_labels.join(', ')}")
expect(conversation.messages.pluck(:content)).to include("#{agent.name} removed #{labels.join(', ')}") expect(Conversations::ActivityMessageJob)
.to(have_been_enqueued.at_least(:once)
.with(conversation, { account_id: conversation.account_id, inbox_id: conversation.inbox_id,
message_type: :activity, content: "#{agent.name} added #{updated_labels.join(', ')}" }))
expect(Conversations::ActivityMessageJob)
.to(have_been_enqueued.at_least(:once)
.with(conversation, { account_id: conversation.account_id, inbox_id: conversation.inbox_id,
message_type: :activity, content: "#{agent.name} removed #{labels.join(', ')}" }))
end end
end end
@ -238,7 +265,9 @@ RSpec.describe Conversation, type: :model do
it 'creates mute message' do it 'creates mute message' do
mute! mute!
expect(conversation.messages.pluck(:content)).to include("#{user.name} has muted the conversation") expect(Conversations::ActivityMessageJob)
.to(have_been_enqueued.at_least(:once).with(conversation, { account_id: conversation.account_id, inbox_id: conversation.inbox_id,
message_type: :activity, content: "#{user.name} has muted the conversation" }))
end end
end end
@ -265,7 +294,9 @@ RSpec.describe Conversation, type: :model do
it 'creates unmute message' do it 'creates unmute message' do
unmute! unmute!
expect(conversation.messages.pluck(:content)).to include("#{user.name} has unmuted the conversation") expect(Conversations::ActivityMessageJob)
.to(have_been_enqueued.at_least(:once).with(conversation, { account_id: conversation.account_id, inbox_id: conversation.inbox_id,
message_type: :activity, content: "#{user.name} has unmuted the conversation" }))
end end
end end

View file

@ -117,6 +117,9 @@ describe ::MessageTemplates::HookExecutionService do
conversation.inbox.update(csat_survey_enabled: true) conversation.inbox.update(csat_survey_enabled: true)
conversation.resolved! conversation.resolved!
Conversations::ActivityMessageJob.perform_now(conversation,
{ account_id: conversation.account_id, inbox_id: conversation.inbox_id, message_type: :activity,
content: 'Conversation marked resolved!!' })
expect(::MessageTemplates::Template::CsatSurvey).to have_received(:new).with(conversation: conversation) expect(::MessageTemplates::Template::CsatSurvey).to have_received(:new).with(conversation: conversation)
expect(csat_survey).to have_received(:perform) expect(csat_survey).to have_received(:perform)
@ -126,6 +129,9 @@ describe ::MessageTemplates::HookExecutionService do
conversation.inbox.update(csat_survey_enabled: false) conversation.inbox.update(csat_survey_enabled: false)
conversation.resolved! conversation.resolved!
Conversations::ActivityMessageJob.perform_now(conversation,
{ account_id: conversation.account_id, inbox_id: conversation.inbox_id, message_type: :activity,
content: 'Conversation marked resolved!!' })
expect(::MessageTemplates::Template::CsatSurvey).not_to have_received(:new).with(conversation: conversation) expect(::MessageTemplates::Template::CsatSurvey).not_to have_received(:new).with(conversation: conversation)
expect(csat_survey).not_to have_received(:perform) expect(csat_survey).not_to have_received(:perform)
@ -138,6 +144,9 @@ describe ::MessageTemplates::HookExecutionService do
conversation.inbox.update(csat_survey_enabled: true) conversation.inbox.update(csat_survey_enabled: true)
conversation.resolved! conversation.resolved!
Conversations::ActivityMessageJob.perform_now(conversation,
{ account_id: conversation.account_id, inbox_id: conversation.inbox_id, message_type: :activity,
content: 'Conversation marked resolved!!' })
expect(::MessageTemplates::Template::CsatSurvey).not_to have_received(:new).with(conversation: conversation) expect(::MessageTemplates::Template::CsatSurvey).not_to have_received(:new).with(conversation: conversation)
expect(csat_survey).not_to have_received(:perform) expect(csat_survey).not_to have_received(:perform)
@ -148,6 +157,9 @@ describe ::MessageTemplates::HookExecutionService do
conversation.messages.create!(message_type: 'outgoing', content_type: :input_csat, account: conversation.account, inbox: conversation.inbox) conversation.messages.create!(message_type: 'outgoing', content_type: :input_csat, account: conversation.account, inbox: conversation.inbox)
conversation.resolved! conversation.resolved!
Conversations::ActivityMessageJob.perform_now(conversation,
{ account_id: conversation.account_id, inbox_id: conversation.inbox_id, message_type: :activity,
content: 'Conversation marked resolved!!' })
expect(::MessageTemplates::Template::CsatSurvey).not_to have_received(:new).with(conversation: conversation) expect(::MessageTemplates::Template::CsatSurvey).not_to have_received(:new).with(conversation: conversation)
expect(csat_survey).not_to have_received(:perform) expect(csat_survey).not_to have_received(:perform)

View file

@ -28,7 +28,7 @@ parameters:
availability_status: availability_status:
type: string type: string
enum: ['available', 'busy', 'offline'] enum: ['available', 'busy', 'offline']
description: The availability status of the agent. description: The availability setting of the agent.
auto_offline: auto_offline:
type: boolean type: boolean
description: Whether the availability status of agent is configured to go offline automatically when away. description: Whether the availability status of agent is configured to go offline automatically when away.

View file

@ -23,10 +23,10 @@ parameters:
enum: ['agent', 'administrator'] enum: ['agent', 'administrator']
description: Whether its administrator or agent description: Whether its administrator or agent
required: true required: true
availability_status: availability:
type: string type: string
enum: ['available', 'busy', 'offline'] enum: ['available', 'busy', 'offline']
description: The availability status of the agent. description: The availability setting of the agent.
auto_offline: auto_offline:
type: boolean type: boolean
description: Whether the availability status of agent is configured to go offline automatically when away. description: Whether the availability status of agent is configured to go offline automatically when away.

View file

@ -1182,7 +1182,7 @@
"busy", "busy",
"offline" "offline"
], ],
"description": "The availability status of the agent." "description": "The availability setting of the agent."
}, },
"auto_offline": { "auto_offline": {
"type": "boolean", "type": "boolean",
@ -1246,14 +1246,14 @@
"description": "Whether its administrator or agent", "description": "Whether its administrator or agent",
"required": true "required": true
}, },
"availability_status": { "availability": {
"type": "string", "type": "string",
"enum": [ "enum": [
"available", "available",
"busy", "busy",
"offline" "offline"
], ],
"description": "The availability status of the agent." "description": "The availability setting of the agent."
}, },
"auto_offline": { "auto_offline": {
"type": "boolean", "type": "boolean",