From 337a74a10ca1a2b4a4127c81c3339d5e591e721d Mon Sep 17 00:00:00 2001 From: Fayaz Ahmed <15716057+fayazara@users.noreply.github.com> Date: Thu, 14 Apr 2022 13:36:55 +0530 Subject: [PATCH] feat: Add send message, fix issues with message conditions (#4423) Co-authored-by: Tejaswini --- .../accounts/automation_rules_controller.rb | 13 ++- .../widgets/AutomationActionInput.vue | 21 ++++- .../AutomationActionTeamMessageInput.vue | 62 +++++++++++++ .../dashboard/i18n/locale/en/automation.json | 4 +- .../settings/automation/AddAutomationRule.vue | 6 +- .../automation/EditAutomationRule.vue | 14 ++- .../dashboard/settings/automation/Index.vue | 2 +- .../settings/automation/constants.js | 91 ++++++++++++++----- app/listeners/automation_rule_listener.rb | 2 +- .../automation_notification_mailer.rb | 24 +---- app/models/automation_rule.rb | 2 +- .../automation_rules/action_service.rb | 19 ++-- .../conditions_filter_service.rb | 5 +- .../automation_rules_controller_spec.rb | 30 +++++- .../automation_rule_listener_spec.rb | 22 ++--- 15 files changed, 231 insertions(+), 86 deletions(-) create mode 100644 app/javascript/dashboard/components/widgets/AutomationActionTeamMessageInput.vue diff --git a/app/controllers/api/v1/accounts/automation_rules_controller.rb b/app/controllers/api/v1/accounts/automation_rules_controller.rb index dc4f7b6c8..41296db62 100644 --- a/app/controllers/api/v1/accounts/automation_rules_controller.rb +++ b/app/controllers/api/v1/accounts/automation_rules_controller.rb @@ -20,9 +20,15 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont def show; end def update - @automation_rule.update(automation_rules_permit) - process_attachments - @automation_rule + ActiveRecord::Base.transaction do + @automation_rule.update!(automation_rules_permit) + @automation_rule.actions = params[:actions] if params[:actions] + @automation_rule.save! + process_attachments + rescue StandardError => e + Rails.logger.error e + render json: { error: @automation_rule.errors.messages }.to_json, status: :unprocessable_entity + end end def destroy @@ -45,6 +51,7 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont params[:attachments].each do |uploaded_attachment| @automation_rule.files.attach(uploaded_attachment) end + @automation_rule end def automation_rules_permit diff --git a/app/javascript/dashboard/components/widgets/AutomationActionInput.vue b/app/javascript/dashboard/components/widgets/AutomationActionInput.vue index dc3a31c08..baa66afb4 100644 --- a/app/javascript/dashboard/components/widgets/AutomationActionInput.vue +++ b/app/javascript/dashboard/components/widgets/AutomationActionInput.vue @@ -7,7 +7,7 @@

+ + diff --git a/app/javascript/dashboard/i18n/locale/en/automation.json b/app/javascript/dashboard/i18n/locale/en/automation.json index 8c92467bd..0d2076b65 100644 --- a/app/javascript/dashboard/i18n/locale/en/automation.json +++ b/app/javascript/dashboard/i18n/locale/en/automation.json @@ -89,7 +89,9 @@ "DELETE_MESSAGE": "You need to have atleast one condition to save" }, "ACTION": { - "DELETE_MESSAGE": "You need to have atleast one action to save" + "DELETE_MESSAGE": "You need to have atleast one action to save", + "TEAM_MESSAGE_INPUT_PLACEHOLDER": "Enter your message here", + "TEAM_DROPDOWN_PLACEHOLDER": "Select teams" }, "TOGGLE": { "ACTIVATION_TITLE": "Activate Automation Rule", diff --git a/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue b/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue index 73a02fd1a..66239b623 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue @@ -192,10 +192,11 @@ export default { $each: { action_params: { required: requiredIf(prop => { + if (prop.action_name === 'send_email_to_team') return true; return !( prop.action_name === 'mute_conversation' || prop.action_name === 'snooze_conversation' || - prop.action_name === 'resolve_convresation' + prop.action_name === 'resolve_conversation' ); }), }, @@ -361,6 +362,7 @@ export default { getActionDropdownValues(type) { switch (type) { case 'assign_team': + case 'send_email_to_team': return this.$store.getters['teams/getTeams']; case 'add_label': return this.$store.getters['labels/getLabels'].map(i => { @@ -443,6 +445,8 @@ export default { return true; }, showActionInput(actionName) { + if (actionName === 'send_email_to_team' || actionName === 'send_message') + return false; const type = AUTOMATION_ACTION_TYPES.find( action => action.key === actionName ).inputType; diff --git a/app/javascript/dashboard/routes/dashboard/settings/automation/EditAutomationRule.vue b/app/javascript/dashboard/routes/dashboard/settings/automation/EditAutomationRule.vue index 14a8e84bb..6c5d03962 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/automation/EditAutomationRule.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/automation/EditAutomationRule.vue @@ -199,7 +199,7 @@ export default { return !( prop.action_name === 'mute_conversation' || prop.action_name === 'snooze_conversation' || - prop.action_name === 'resolve_convresation' + prop.action_name === 'resolve_conversation' ); }), }, @@ -360,6 +360,7 @@ export default { getActionDropdownValues(type) { switch (type) { case 'assign_team': + case 'send_email_to_team': return this.$store.getters['teams/getTeams']; case 'add_label': return this.$store.getters['labels/getLabels'].map(i => { @@ -475,6 +476,15 @@ export default { actionParams = [ ...this.getActionDropdownValues(action.action_name), ].filter(item => [...action.action_params].includes(item.id)); + } else if (inputType === 'team_message') { + actionParams = { + team_ids: [ + ...this.getActionDropdownValues(action.action_name), + ].filter(item => + [...action.action_params[0].team_ids].includes(item.id) + ), + message: action.action_params[0].message, + }; } else actionParams = [...action.action_params]; } return { @@ -489,6 +499,8 @@ export default { }; }, showActionInput(actionName) { + if (actionName === 'send_email_to_team' || actionName === 'send_message') + return false; const type = AUTOMATION_ACTION_TYPES.find( action => action.key === actionName ).inputType; diff --git a/app/javascript/dashboard/routes/dashboard/settings/automation/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/automation/Index.vue index f028933cb..28b8c982f 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/automation/Index.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/automation/Index.vue @@ -253,7 +253,7 @@ export default { : this.$t('AUTOMATION.TOGGLE.ACTIVATION_DESCRIPTION', { automationName: automation.name, }); - // Check if uses confirms to proceed + // Check if user confirms to proceed const ok = await this.$refs.confirmDialog.showConfirmation(); if (ok) { await await this.$store.dispatch('automations/update', { diff --git a/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js b/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js index cd306007a..bdec6ebc0 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js +++ b/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js @@ -76,11 +76,16 @@ export const AUTOMATIONS = { name: 'Add a label', attributeI18nKey: 'ADD_LABEL', }, - // { - // key: 'send_email_to_team', - // name: 'Send an email to team', - // attributeI18nKey: 'SEND_MESSAGE', - // }, + { + key: 'send_email_to_team', + name: 'Send an email to team', + attributeI18nKey: 'SEND_EMAIL_TO_TEAM', + }, + { + key: 'send_message', + name: 'Send a message', + attributeI18nKey: 'SEND_MESSAGE', + }, { key: 'send_email_transcript', name: 'Send an email transcript', @@ -96,8 +101,9 @@ export const AUTOMATIONS = { name: 'Snooze conversation', attributeI18nKey: 'MUTE_CONVERSATION', }, + { - key: 'resolve_convresation', + key: 'resolve_conversation', name: 'Resolve conversation', attributeI18nKey: 'RESOLVE_CONVERSATION', }, @@ -106,6 +112,11 @@ export const AUTOMATIONS = { name: 'Send Webhook Event', attributeI18nKey: 'SEND_WEBHOOK_EVENT', }, + // { + // key: 'send_attachment', + // name: 'Send Attachment', + // attributeI18nKey: 'SEND_ATTACHMENT', + // }, ], }, conversation_created: { @@ -132,7 +143,7 @@ export const AUTOMATIONS = { filterOperators: OPERATOR_TYPES_1, }, { - key: 'referrer', + key: 'referer', name: 'Referrer Link', attributeI18nKey: 'REFERER_LINK', inputType: 'plain_text', @@ -150,11 +161,16 @@ export const AUTOMATIONS = { name: 'Assign an agent', attributeI18nKey: 'ASSIGN_AGENT', }, - // { - // key: 'send_email_to_team', - // name: 'Send an email to team', - // attributeI18nKey: 'SEND_MESSAGE', - // }, + { + key: 'send_email_to_team', + name: 'Send an email to team', + attributeI18nKey: 'SEND_EMAIL_TO_TEAM', + }, + { + key: 'send_message', + name: 'Send a message', + attributeI18nKey: 'SEND_MESSAGE', + }, { key: 'send_email_transcript', name: 'Send an email transcript', @@ -171,7 +187,7 @@ export const AUTOMATIONS = { attributeI18nKey: 'MUTE_CONVERSATION', }, { - key: 'resolve_convresation', + key: 'resolve_conversation', name: 'Resolve conversation', attributeI18nKey: 'RESOLVE_CONVERSATION', }, @@ -180,6 +196,11 @@ export const AUTOMATIONS = { name: 'Send Webhook Event', attributeI18nKey: 'SEND_WEBHOOK_EVENT', }, + // { + // key: 'send_attachment', + // name: 'Send Attachment', + // attributeI18nKey: 'SEND_ATTACHMENT', + // }, ], }, conversation_updated: { @@ -238,11 +259,16 @@ export const AUTOMATIONS = { name: 'Assign an agent', attributeI18nKey: 'ASSIGN_AGENT', }, - // { - // key: 'send_email_to_team', - // name: 'Send an email to team', - // attributeI18nKey: 'SEND_MESSAGE', - // }, + { + key: 'send_email_to_team', + name: 'Send an email to team', + attributeI18nKey: 'SEND_EMAIL_TO_TEAM', + }, + { + key: 'send_message', + name: 'Send a message', + attributeI18nKey: 'SEND_MESSAGE', + }, { key: 'send_email_transcript', name: 'Send an email transcript', @@ -259,7 +285,7 @@ export const AUTOMATIONS = { attributeI18nKey: 'MUTE_CONVERSATION', }, { - key: 'resolve_convresation', + key: 'resolve_conversation', name: 'Resolve conversation', attributeI18nKey: 'RESOLVE_CONVERSATION', }, @@ -268,6 +294,11 @@ export const AUTOMATIONS = { name: 'Send Webhook Event', attributeI18nKey: 'SEND_WEBHOOK_EVENT', }, + // { + // key: 'send_attachment', + // name: 'Send Attachment', + // attributeI18nKey: 'SEND_ATTACHMENT', + // }, ], }, }; @@ -298,11 +329,11 @@ export const AUTOMATION_ACTION_TYPES = [ label: 'Add a label', inputType: 'multi_select', }, - // { - // key: 'send_email_to_team', - // label: 'Send an email to team', - // inputType: 'multi_select', - // }, + { + key: 'send_email_to_team', + label: 'Send an email to team', + inputType: 'team_message', + }, { key: 'send_email_transcript', label: 'Send an email transcript', @@ -319,7 +350,7 @@ export const AUTOMATION_ACTION_TYPES = [ inputType: null, }, { - key: 'resolve_convresation', + key: 'resolve_conversation', label: 'Resolve conversation', inputType: null, }, @@ -328,4 +359,14 @@ export const AUTOMATION_ACTION_TYPES = [ label: 'Send Webhook Event', inputType: 'url', }, + // { + // key: 'send_attachment', + // label: 'Send Attachment', + // inputType: 'file', + // }, + { + key: 'send_message', + label: 'Send a message', + inputType: 'textarea', + }, ]; diff --git a/app/listeners/automation_rule_listener.rb b/app/listeners/automation_rule_listener.rb index c8c021a4e..6ebc46666 100644 --- a/app/listeners/automation_rule_listener.rb +++ b/app/listeners/automation_rule_listener.rb @@ -36,7 +36,7 @@ class AutomationRuleListener < BaseListener return unless rule_present?('message_created', account) @rules.each do |rule| - conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, message.conversation).message_conditions + conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, message.conversation, { message: message }).message_conditions ::AutomationRules::ActionService.new(rule, account, message.conversation).perform if conditions_match.present? end end diff --git a/app/mailers/team_notifications/automation_notification_mailer.rb b/app/mailers/team_notifications/automation_notification_mailer.rb index d6ca47f81..f1bf1ab40 100644 --- a/app/mailers/team_notifications/automation_notification_mailer.rb +++ b/app/mailers/team_notifications/automation_notification_mailer.rb @@ -7,29 +7,7 @@ class TeamNotifications::AutomationNotificationMailer < ApplicationMailer @custom_message = message @action_url = app_account_conversation_url(account_id: @conversation.account_id, id: @conversation.display_id) - send_an_email_to_team and return - end - - def conversation_updated(conversation, team, message) - return unless smtp_config_set_or_development? - - @agents = team.team_members - @conversation = conversation - @custom_message = message - @action_url = app_account_conversation_url(account_id: @conversation.account_id, id: @conversation.display_id) - - send_an_email_to_team and return - end - - def message_created(conversation, team, message) - return unless smtp_config_set_or_development? - - @agents = team.team_members - @conversation = conversation - @custom_message = message - @action_url = app_account_conversation_url(account_id: @conversation.account_id, id: @conversation.display_id) - - send_an_email_to_team and return + send_an_email_to_team end private diff --git a/app/models/automation_rule.rb b/app/models/automation_rule.rb index 95cd9543e..1dcc2583d 100644 --- a/app/models/automation_rule.rb +++ b/app/models/automation_rule.rb @@ -27,7 +27,7 @@ class AutomationRule < ApplicationRecord scope :active, -> { where(active: true) } - CONDITIONS_ATTRS = %w[content email country_code status message_type browser_language assignee_id team_id referrer city company].freeze + CONDITIONS_ATTRS = %w[content email country_code status message_type browser_language assignee_id team_id referer city company].freeze ACTIONS_ATTRS = %w[send_message add_label send_email_to_team assign_team assign_best_agents send_attachments].freeze private diff --git a/app/services/automation_rules/action_service.rb b/app/services/automation_rules/action_service.rb index 1249bd9c5..0771f817d 100644 --- a/app/services/automation_rules/action_service.rb +++ b/app/services/automation_rules/action_service.rb @@ -22,8 +22,6 @@ class AutomationRules::ActionService private def send_attachments(_file_params) - return if @rule.event_name == 'message_created' - blobs = @rule.files.map { |file, _| file.blob } params = { content: nil, private: false, attachments: blobs } mb = Messages::MessageBuilder.new(nil, @conversation, params) @@ -44,6 +42,10 @@ class AutomationRules::ActionService @conversation.snoozed! end + def resolve_conversation(_params) + @conversation.resolved! + end + def change_status(status) @conversation.update!(status: status[0]) end @@ -54,8 +56,6 @@ class AutomationRules::ActionService end def send_message(message) - return if @rule.event_name == 'message_created' - params = { content: message[0], private: false } mb = Messages::MessageBuilder.new(nil, @conversation, params) mb.perform @@ -82,15 +82,10 @@ class AutomationRules::ActionService end def send_email_to_team(params) - team = Team.find(params[:team_ids][0]) + teams = Team.where(id: params[0][:team_ids]) - case @rule.event_name - when 'conversation_created', 'conversation_status_changed' - TeamNotifications::AutomationNotificationMailer.conversation_creation(@conversation, team, params[:message])&.deliver_now - when 'conversation_updated' - TeamNotifications::AutomationNotificationMailer.conversation_updated(@conversation, team, params[:message])&.deliver_now - when 'message_created' - TeamNotifications::AutomationNotificationMailer.message_created(@conversation, team, params[:message])&.deliver_now + teams.each do |team| + TeamNotifications::AutomationNotificationMailer.conversation_creation(@conversation, team, params[0][:message])&.deliver_now end end diff --git a/app/services/automation_rules/conditions_filter_service.rb b/app/services/automation_rules/conditions_filter_service.rb index 46988771d..2ac011fbd 100644 --- a/app/services/automation_rules/conditions_filter_service.rb +++ b/app/services/automation_rules/conditions_filter_service.rb @@ -3,13 +3,14 @@ require 'json' class AutomationRules::ConditionsFilterService < FilterService ATTRIBUTE_MODEL = 'contact_attribute'.freeze - def initialize(rule, conversation = nil) + def initialize(rule, conversation = nil, options = {}) super([], nil) @rule = rule @conversation = conversation @account = conversation.account file = File.read('./lib/filters/filter_keys.json') @filters = JSON.parse(file) + @options = options end def perform @@ -41,7 +42,7 @@ class AutomationRules::ConditionsFilterService < FilterService current_filter = message_filters[query_hash['attribute_key']] @query_string += message_query_string(current_filter, query_hash.with_indifferent_access, current_index) end - records = Message.where(conversation: @conversation).where(@query_string, @filter_values.with_indifferent_access) + records = Message.where(id: @options[:message].id).where(@query_string, @filter_values.with_indifferent_access) records.any? end diff --git a/spec/controllers/api/v1/accounts/automation_rules_controller_spec.rb b/spec/controllers/api/v1/accounts/automation_rules_controller_spec.rb index 0b921bc07..2039daf15 100644 --- a/spec/controllers/api/v1/accounts/automation_rules_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/automation_rules_controller_spec.rb @@ -259,17 +259,41 @@ RSpec.describe 'Api::V1::Accounts::AutomationRulesController', type: :request do end context 'when it is an authenticated user' do - it 'returns for updated automation_rule for account' do - params = { name: 'Update name' } + let(:update_params) do + { + description: 'Update description', + name: 'Update name', + conditions: [ + { + attribute_key: 'browser_language', + filter_operator: 'equal_to', + values: ['en'], + query_operator: 'AND' + } + ], + actions: [ + { + action_name: :update_additional_attributes, + action_params: [{ intiated_at: '2021-12-03 17:25:26.844536 +0530' }] + } + ] + }.with_indifferent_access + end + + it 'returns for cloned automation_rule for account' do expect(account.automation_rules.count).to eq(1) + expect(account.automation_rules.first.actions.size).to eq(4) patch "/api/v1/accounts/#{account.id}/automation_rules/#{automation_rule.id}", headers: administrator.create_new_auth_token, - params: params + params: update_params expect(response).to have_http_status(:success) body = JSON.parse(response.body, symbolize_names: true) expect(body[:payload][:name]).to eq('Update name') + expect(body[:payload][:description]).to eq('Update description') + expect(body[:payload][:conditions].size).to eq(1) + expect(body[:payload][:actions].size).to eq(1) end it 'returns for updated active flag for automation_rule' do diff --git a/spec/listeners/automation_rule_listener_spec.rb b/spec/listeners/automation_rule_listener_spec.rb index b0ed4d0bd..6bbffe077 100644 --- a/spec/listeners/automation_rule_listener_spec.rb +++ b/spec/listeners/automation_rule_listener_spec.rb @@ -30,10 +30,10 @@ describe AutomationRuleListener do automation_rule.update!(actions: [ { - 'action_name' => 'send_email_to_team', 'action_params' => { + 'action_name' => 'send_email_to_team', 'action_params' => [{ 'message' => 'Please pay attention to this conversation, its from high priority customer', 'team_ids' => [team.id] - } + }] }, { 'action_name' => 'assign_team', 'action_params' => [team.id] }, { 'action_name' => 'add_label', 'action_params' => %w[support priority_customer] }, @@ -104,7 +104,7 @@ describe AutomationRuleListener do it 'triggers automation rule send email transcript to the mentioned email' do mailer = double - expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_updated) + expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation) listener.conversation_updated(event) @@ -116,7 +116,7 @@ describe AutomationRuleListener do it 'triggers automation rule send message to the contacts' do expect(conversation.messages).to be_empty - expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_updated) + expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation) listener.conversation_updated(event) @@ -202,7 +202,7 @@ describe AutomationRuleListener do automation_rule - expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_updated) + expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation) listener.conversation_updated(event) @@ -214,7 +214,7 @@ describe AutomationRuleListener do it 'triggers automation rule send email to the team' do automation_rule - expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_updated) + expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation) listener.conversation_updated(event) end @@ -224,7 +224,7 @@ describe AutomationRuleListener do automation_rule - expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_updated) + expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation) listener.conversation_updated(event) @@ -256,7 +256,7 @@ describe AutomationRuleListener do automation_rule - expect(TeamNotifications::AutomationNotificationMailer).to receive(:message_created) + expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation) listener.message_created(event) @@ -269,7 +269,7 @@ describe AutomationRuleListener do automation_rule - expect(TeamNotifications::AutomationNotificationMailer).to receive(:message_created) + expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation) listener.message_created(event) @@ -281,7 +281,7 @@ describe AutomationRuleListener do expect(conversation.assignee).to be_nil automation_rule - expect(TeamNotifications::AutomationNotificationMailer).to receive(:message_created) + expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation) listener.message_created(event) @@ -295,7 +295,7 @@ describe AutomationRuleListener do automation_rule - expect(TeamNotifications::AutomationNotificationMailer).to receive(:message_created) + expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation) listener.message_created(event)