chore: Change the conversation bot status to pending (#2677)

fixes: #2649
This commit is contained in:
Sojan Jose 2021-07-21 22:02:43 +05:30 committed by GitHub
parent a0886d37bc
commit a7ca55c080
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 87 additions and 54 deletions

View file

@ -49,7 +49,8 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
def toggle_status def toggle_status
if params[:status] if params[:status]
@conversation.status = params[:status] status = params[:status] == 'bot' ? 'pending' : params[:status]
@conversation.status = status
@status = @conversation.save @status = @conversation.save
else else
@status = @conversation.toggle_status @status = @conversation.toggle_status
@ -106,6 +107,9 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
def conversation_params def conversation_params
additional_attributes = params[:additional_attributes]&.permit! || {} additional_attributes = params[:additional_attributes]&.permit! || {}
status = params[:status].present? ? { status: params[:status] } : {} status = params[:status].present? ? { status: params[:status] } : {}
# TODO: temporary fallback for the old bot status in conversation, we will remove after couple of releases
status = { status: 'pending' } if status[:status] == 'bot'
{ {
account_id: Current.account.id, account_id: Current.account.id,
inbox_id: @contact_inbox.inbox_id, inbox_id: @contact_inbox.inbox_id,

View file

@ -24,7 +24,7 @@
{{ this.$t('CONVERSATION.HEADER.REOPEN_ACTION') }} {{ this.$t('CONVERSATION.HEADER.REOPEN_ACTION') }}
</woot-button> </woot-button>
<woot-button <woot-button
v-else-if="isBot" v-else-if="isPending"
class-names="resolve" class-names="resolve"
color-scheme="primary" color-scheme="primary"
icon="ion-person" icon="ion-person"
@ -48,12 +48,12 @@
class="dropdown-pane dropdown-pane--open" class="dropdown-pane dropdown-pane--open"
> >
<woot-dropdown-menu> <woot-dropdown-menu>
<woot-dropdown-item v-if="!isBot"> <woot-dropdown-item v-if="!isPending">
<woot-button <woot-button
variant="clear" variant="clear"
@click="() => toggleStatus(STATUS_TYPE.BOT)" @click="() => toggleStatus(STATUS_TYPE.PENDING)"
> >
{{ this.$t('CONVERSATION.RESOLVE_DROPDOWN.OPEN_BOT') }} {{ this.$t('CONVERSATION.RESOLVE_DROPDOWN.MARK_PENDING') }}
</woot-button> </woot-button>
</woot-dropdown-item> </woot-dropdown-item>
</woot-dropdown-menu> </woot-dropdown-menu>
@ -91,20 +91,20 @@ export default {
isOpen() { isOpen() {
return this.currentChat.status === wootConstants.STATUS_TYPE.OPEN; return this.currentChat.status === wootConstants.STATUS_TYPE.OPEN;
}, },
isBot() { isPending() {
return this.currentChat.status === wootConstants.STATUS_TYPE.BOT; return this.currentChat.status === wootConstants.STATUS_TYPE.PENDING;
}, },
isResolved() { isResolved() {
return this.currentChat.status === wootConstants.STATUS_TYPE.RESOLVED; return this.currentChat.status === wootConstants.STATUS_TYPE.RESOLVED;
}, },
buttonClass() { buttonClass() {
if (this.isBot) return 'primary'; if (this.isPending) return 'primary';
if (this.isOpen) return 'success'; if (this.isOpen) return 'success';
if (this.isResolved) return 'warning'; if (this.isResolved) return 'warning';
return ''; return '';
}, },
showDropDown() { showDropDown() {
return !this.isBot; return !this.isPending;
}, },
}, },
methods: { methods: {

View file

@ -8,7 +8,7 @@ export default {
STATUS_TYPE: { STATUS_TYPE: {
OPEN: 'open', OPEN: 'open',
RESOLVED: 'resolved', RESOLVED: 'resolved',
BOT: 'bot', PENDING: 'pending',
}, },
}; };
export const DEFAULT_REDIRECT_URL = '/app/'; export const DEFAULT_REDIRECT_URL = '/app/';

View file

@ -47,8 +47,8 @@
"VALUE": "resolved" "VALUE": "resolved"
}, },
{ {
"TEXT": "Bot", "TEXT": "Pending",
"VALUE": "bot" "VALUE": "pending"
} }
], ],
"ATTACHMENTS": { "ATTACHMENTS": {

View file

@ -41,7 +41,7 @@
"DETAILS": "details" "DETAILS": "details"
}, },
"RESOLVE_DROPDOWN": { "RESOLVE_DROPDOWN": {
"OPEN_BOT": "Open with bot" "MARK_PENDING": "Mark as pending"
}, },
"FOOTER": { "FOOTER": {
"MSG_INPUT": "Shift + enter for new line. Start with '/' to select a Canned Response.", "MSG_INPUT": "Shift + enter for new line. Start with '/' to select a Canned Response.",

View file

@ -7,10 +7,10 @@ jest.mock('widget/helpers/axios');
describe('#actions', () => { describe('#actions', () => {
describe('#get attributes', () => { describe('#get attributes', () => {
it('sends mutation if api is success', async () => { it('sends mutation if api is success', async () => {
API.get.mockResolvedValue({ data: { id: 1, status: 'bot' } }); API.get.mockResolvedValue({ data: { id: 1, status: 'pending' } });
await actions.getAttributes({ commit }); await actions.getAttributes({ commit });
expect(commit.mock.calls).toEqual([ expect(commit.mock.calls).toEqual([
['SET_CONVERSATION_ATTRIBUTES', { id: 1, status: 'bot' }], ['SET_CONVERSATION_ATTRIBUTES', { id: 1, status: 'pending' }],
['conversation/setMetaUserLastSeenAt', undefined, { root: true }], ['conversation/setMetaUserLastSeenAt', undefined, { root: true }],
]); ]);
}); });
@ -23,10 +23,10 @@ describe('#actions', () => {
describe('#update attributes', () => { describe('#update attributes', () => {
it('sends correct mutations', () => { it('sends correct mutations', () => {
actions.update({ commit }, { id: 1, status: 'bot' }); actions.update({ commit }, { id: 1, status: 'pending' });
expect(commit).toBeCalledWith('UPDATE_CONVERSATION_ATTRIBUTES', { expect(commit).toBeCalledWith('UPDATE_CONVERSATION_ATTRIBUTES', {
id: 1, id: 1,
status: 'bot', status: 'pending',
}); });
}); });
}); });

View file

@ -4,11 +4,11 @@ describe('#getters', () => {
it('getConversationParams', () => { it('getConversationParams', () => {
const state = { const state = {
id: 1, id: 1,
status: 'bot', status: 'pending',
}; };
expect(getters.getConversationParams(state)).toEqual({ expect(getters.getConversationParams(state)).toEqual({
id: 1, id: 1,
status: 'bot', status: 'pending',
}); });
}); });
}); });

View file

@ -14,7 +14,7 @@ describe('#mutations', () => {
describe('#UPDATE_CONVERSATION_ATTRIBUTES', () => { describe('#UPDATE_CONVERSATION_ATTRIBUTES', () => {
it('update status if it is same conversation', () => { it('update status if it is same conversation', () => {
const state = { id: 1, status: 'bot' }; const state = { id: 1, status: 'pending' };
mutations.UPDATE_CONVERSATION_ATTRIBUTES(state, { mutations.UPDATE_CONVERSATION_ATTRIBUTES(state, {
id: 1, id: 1,
status: 'open', status: 'open',
@ -22,12 +22,12 @@ describe('#mutations', () => {
expect(state).toEqual({ id: 1, status: 'open' }); expect(state).toEqual({ id: 1, status: 'open' });
}); });
it('doesnot update status if it is not the same conversation', () => { it('doesnot update status if it is not the same conversation', () => {
const state = { id: 1, status: 'bot' }; const state = { id: 1, status: 'pending' };
mutations.UPDATE_CONVERSATION_ATTRIBUTES(state, { mutations.UPDATE_CONVERSATION_ATTRIBUTES(state, {
id: 2, id: 2,
status: 'open', status: 'open',
}); });
expect(state).toEqual({ id: 1, status: 'bot' }); expect(state).toEqual({ id: 1, status: 'pending' });
}); });
}); });

View file

@ -1,7 +1,7 @@
class NotificationListener < BaseListener class NotificationListener < BaseListener
def conversation_created(event) def conversation_created(event)
conversation, account = extract_conversation_and_account(event) conversation, account = extract_conversation_and_account(event)
return if conversation.bot? return if conversation.pending?
conversation.inbox.members.each do |agent| conversation.inbox.members.each do |agent|
NotificationBuilder.new( NotificationBuilder.new(
@ -17,7 +17,7 @@ class NotificationListener < BaseListener
conversation, account = extract_conversation_and_account(event) conversation, account = extract_conversation_and_account(event)
assignee = conversation.assignee assignee = conversation.assignee
return unless conversation.notifiable_assignee_change? return unless conversation.notifiable_assignee_change?
return if conversation.bot? return if conversation.pending?
NotificationBuilder.new( NotificationBuilder.new(
notification_type: 'conversation_assignment', notification_type: 'conversation_assignment',

View file

@ -45,7 +45,7 @@ class Conversation < ApplicationRecord
validates :inbox_id, presence: true validates :inbox_id, presence: true
before_validation :validate_additional_attributes before_validation :validate_additional_attributes
enum status: { open: 0, resolved: 1, bot: 2 } enum status: { open: 0, resolved: 1, pending: 2 }
scope :latest, -> { order(last_activity_at: :desc) } scope :latest, -> { order(last_activity_at: :desc) }
scope :unassigned, -> { where(assignee_id: nil) } scope :unassigned, -> { where(assignee_id: nil) }
@ -64,7 +64,7 @@ class Conversation < ApplicationRecord
has_one :csat_survey_response, dependent: :destroy has_one :csat_survey_response, dependent: :destroy
has_many :notifications, as: :primary_actor, dependent: :destroy has_many :notifications, as: :primary_actor, dependent: :destroy
before_create :set_bot_conversation before_create :mark_conversation_pending_if_bot
# wanted to change this to after_update commit. But it ended up creating a loop # wanted to change this to after_update commit. But it ended up creating a loop
# reinvestigate in future and identity the implications # reinvestigate in future and identity the implications
@ -91,7 +91,7 @@ class Conversation < ApplicationRecord
def toggle_status def toggle_status
# FIXME: implement state machine with aasm # FIXME: implement state machine with aasm
self.status = open? ? :resolved : :open self.status = open? ? :resolved : :open
self.status = :open if bot? self.status = :open if pending?
save save
end end
@ -144,8 +144,9 @@ class Conversation < ApplicationRecord
self.additional_attributes = {} unless additional_attributes.is_a?(Hash) self.additional_attributes = {} unless additional_attributes.is_a?(Hash)
end end
def set_bot_conversation def mark_conversation_pending_if_bot
self.status = :bot if inbox.agent_bot_inbox&.active? || inbox.hooks.pluck(:app_id).include?('dialogflow') # TODO: make this an inbox config instead of assuming bot conversations should start as pending
self.status = :pending if inbox.agent_bot_inbox&.active? || inbox.hooks.pluck(:app_id).include?('dialogflow')
end end
def notify_conversation_creation def notify_conversation_creation

View file

@ -63,7 +63,7 @@ en:
status: status:
resolved: "Conversation was marked resolved by %{user_name}" resolved: "Conversation was marked resolved by %{user_name}"
open: "Conversation was reopened by %{user_name}" open: "Conversation was reopened by %{user_name}"
bot: "Conversation was transferred to bot by %{user_name}" pending: "Conversation was marked as pending by %{user_name}"
auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity" auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity"
assignee: assignee:
self_assigned: "%{user_name} self-assigned this conversation" self_assigned: "%{user_name} self-assigned this conversation"

View file

@ -5,7 +5,7 @@ class Integrations::Dialogflow::ProcessorService
message = event_data[:message] message = event_data[:message]
return if message.private? return if message.private?
return unless processable_message?(message) return unless processable_message?(message)
return unless message.conversation.bot? return unless message.conversation.pending?
response = get_dialogflow_response(message.conversation.contact_inbox.source_id, message_content(message)) response = get_dialogflow_response(message.conversation.contact_inbox.source_id, message_content(message))
process_response(message, response) process_response(message, response)

View file

@ -32,7 +32,7 @@ RSpec.describe 'API Base', type: :request do
describe 'request with api_access_token for bot' do describe 'request with api_access_token for bot' do
let!(:agent_bot) { create(:agent_bot) } let!(:agent_bot) { create(:agent_bot) }
let!(:inbox) { create(:inbox, account: account) } let!(:inbox) { create(:inbox, account: account) }
let!(:conversation) { create(:conversation, account: account, inbox: inbox, assignee: user, status: 'bot') } let!(:conversation) { create(:conversation, account: account, inbox: inbox, assignee: user, status: 'pending') }
context 'when it is an unauthorized url' do context 'when it is an unauthorized url' do
it 'returns unauthorized' do it 'returns unauthorized' do

View file

@ -200,6 +200,20 @@ RSpec.describe 'Conversations API', type: :request do
end end
it 'creates a conversation in specificed status' do it 'creates a conversation in specificed status' do
allow(Rails.configuration.dispatcher).to receive(:dispatch)
post "/api/v1/accounts/#{account.id}/conversations",
headers: agent.create_new_auth_token,
params: { source_id: contact_inbox.source_id, status: 'pending' },
as: :json
expect(response).to have_http_status(:success)
response_data = JSON.parse(response.body, symbolize_names: true)
expect(response_data[:status]).to eq('pending')
end
# TODO: remove this spec when we remove the condition check in controller
# Added for backwards compatibility for bot status
it 'creates a conversation as pending if status is specified as bot' do
allow(Rails.configuration.dispatcher).to receive(:dispatch) allow(Rails.configuration.dispatcher).to receive(:dispatch)
post "/api/v1/accounts/#{account.id}/conversations", post "/api/v1/accounts/#{account.id}/conversations",
headers: agent.create_new_auth_token, headers: agent.create_new_auth_token,
@ -208,7 +222,7 @@ RSpec.describe 'Conversations API', type: :request do
expect(response).to have_http_status(:success) expect(response).to have_http_status(:success)
response_data = JSON.parse(response.body, symbolize_names: true) response_data = JSON.parse(response.body, symbolize_names: true)
expect(response_data[:status]).to eq('bot') expect(response_data[:status]).to eq('pending')
end end
it 'creates a new conversation with message when message is passed' do it 'creates a new conversation with message when message is passed' do
@ -269,8 +283,8 @@ RSpec.describe 'Conversations API', type: :request do
expect(conversation.reload.status).to eq('resolved') expect(conversation.reload.status).to eq('resolved')
end end
it 'toggles the conversation status to open from bot' do it 'toggles the conversation status to open from pending' do
conversation.update!(status: 'bot') conversation.update!(status: 'pending')
post "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/toggle_status", post "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/toggle_status",
headers: agent.create_new_auth_token, headers: agent.create_new_auth_token,
@ -283,13 +297,27 @@ RSpec.describe 'Conversations API', type: :request do
it 'toggles the conversation status to specific status when parameter is passed' do it 'toggles the conversation status to specific status when parameter is passed' do
expect(conversation.status).to eq('open') expect(conversation.status).to eq('open')
post "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/toggle_status",
headers: agent.create_new_auth_token,
params: { status: 'pending' },
as: :json
expect(response).to have_http_status(:success)
expect(conversation.reload.status).to eq('pending')
end
# TODO: remove this spec when we remove the condition check in controller
# Added for backwards compatibility for bot status
it 'toggles the conversation status to pending status when parameter bot is passed' do
expect(conversation.status).to eq('open')
post "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/toggle_status", post "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/toggle_status",
headers: agent.create_new_auth_token, headers: agent.create_new_auth_token,
params: { status: 'bot' }, params: { status: 'bot' },
as: :json as: :json
expect(response).to have_http_status(:success) expect(response).to have_http_status(:success)
expect(conversation.reload.status).to eq('bot') expect(conversation.reload.status).to eq('pending')
end end
end end
end end

View file

@ -3,7 +3,7 @@ require 'rails_helper'
describe Integrations::Dialogflow::ProcessorService do describe Integrations::Dialogflow::ProcessorService do
let(:account) { create(:account) } let(:account) { create(:account) }
let(:hook) { create(:integrations_hook, :dialogflow, account: account) } let(:hook) { create(:integrations_hook, :dialogflow, account: account) }
let(:conversation) { create(:conversation, account: account, status: :bot) } let(:conversation) { create(:conversation, account: account, status: :pending) }
let(:message) { create(:message, account: account, conversation: conversation) } let(:message) { create(:message, account: account, conversation: conversation) }
let(:event_name) { 'message.created' } let(:event_name) { 'message.created' }
let(:event_data) { { message: message } } let(:event_data) { { message: message } }

View file

@ -40,7 +40,7 @@ shared_examples_for 'round_robin_handler' do
account: account, account: account,
contact: create(:contact, account: account), contact: create(:contact, account: account),
inbox: inbox, inbox: inbox,
status: 'bot', status: 'pending',
assignee: nil assignee: nil
) )

View file

@ -345,8 +345,8 @@ RSpec.describe Conversation, type: :model do
let!(:bot_inbox) { create(:agent_bot_inbox) } let!(:bot_inbox) { create(:agent_bot_inbox) }
let(:conversation) { create(:conversation, inbox: bot_inbox.inbox) } let(:conversation) { create(:conversation, inbox: bot_inbox.inbox) }
it 'returns conversation status as bot' do it 'returns conversation status as pending' do
expect(conversation.status).to eq('bot') expect(conversation.status).to eq('pending')
end end
end end
@ -354,8 +354,8 @@ RSpec.describe Conversation, type: :model do
let(:hook) { create(:integrations_hook, :dialogflow) } let(:hook) { create(:integrations_hook, :dialogflow) }
let(:conversation) { create(:conversation, inbox: hook.inbox) } let(:conversation) { create(:conversation, inbox: hook.inbox) }
it 'returns conversation status as bot' do it 'returns conversation status as pending' do
expect(conversation.status).to eq('bot') expect(conversation.status).to eq('pending')
end end
end end

View file

@ -13,7 +13,7 @@ properties:
description: ID of the inbox description: ID of the inbox
status: status:
type: string type: string
enum: ['open', 'resolved', 'bot'] enum: ['open', 'resolved', 'pending']
description: The status of the conversation description: The status of the conversation
timestamp: timestamp:
type: string type: string

View file

@ -14,7 +14,7 @@ get:
- name: status - name: status
in: query in: query
type: string type: string
enum: ['open', 'resolved', 'bot'] enum: ['open', 'resolved', 'pending']
required: true required: true
- name: page - name: page
in: query in: query

View file

@ -15,7 +15,7 @@ get:
- name: status - name: status
in: query in: query
type: string type: string
enum: ['open', 'resolved', 'bot'] enum: ['open', 'resolved', 'pending']
- name: page - name: page
in: query in: query
type: integer type: integer
@ -71,8 +71,8 @@ post:
description: Lets you specify attributes like browser information description: Lets you specify attributes like browser information
status: status:
type: string type: string
enum: ['open', 'resolved', 'bot'] enum: ['open', 'resolved', 'pending']
description: Specify the conversation whether it's bot, open, closed description: Specify the conversation whether it's pending, open, closed
responses: responses:
200: 200:

View file

@ -15,7 +15,7 @@ parameters:
properties: properties:
status: status:
type: string type: string
enum: ["open", "resolved", "bot"] enum: ["open", "resolved", "pending"]
required: true required: true
description: The status of the conversation description: The status of the conversation
responses: responses:

View file

@ -1417,7 +1417,7 @@
"enum": [ "enum": [
"open", "open",
"resolved", "resolved",
"bot" "pending"
] ]
}, },
{ {
@ -1508,9 +1508,9 @@
"enum": [ "enum": [
"open", "open",
"resolved", "resolved",
"bot" "pending"
], ],
"description": "Specify the conversation whether it's bot, open, closed" "description": "Specify the conversation whether it's pending, open, closed"
} }
} }
} }
@ -1574,7 +1574,7 @@
"enum": [ "enum": [
"open", "open",
"resolved", "resolved",
"bot" "pending"
], ],
"required": true "required": true
}, },
@ -1688,7 +1688,7 @@
"enum": [ "enum": [
"open", "open",
"resolved", "resolved",
"bot" "pending"
], ],
"required": true, "required": true,
"description": "The status of the conversation" "description": "The status of the conversation"
@ -2850,7 +2850,7 @@
"enum": [ "enum": [
"open", "open",
"resolved", "resolved",
"bot" "pending"
], ],
"description": "The status of the conversation" "description": "The status of the conversation"
}, },