fix: Automations condition based quoted text (#7272)
This commit is contained in:
@@ -2,23 +2,24 @@
|
|||||||
#
|
#
|
||||||
# Table name: messages
|
# Table name: messages
|
||||||
#
|
#
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# additional_attributes :jsonb
|
# additional_attributes :jsonb
|
||||||
# content :text
|
# content :text
|
||||||
# content_attributes :json
|
# content_attributes :json
|
||||||
# content_type :integer default("text"), not null
|
# content_type :integer default("text"), not null
|
||||||
# external_source_ids :jsonb
|
# external_source_ids :jsonb
|
||||||
# message_type :integer not null
|
# message_type :integer not null
|
||||||
# private :boolean default(FALSE)
|
# private :boolean default(FALSE)
|
||||||
# sender_type :string
|
# processed_message_content :text
|
||||||
# status :integer default("sent")
|
# sender_type :string
|
||||||
# created_at :datetime not null
|
# status :integer default("sent")
|
||||||
# updated_at :datetime not null
|
# created_at :datetime not null
|
||||||
# account_id :integer not null
|
# updated_at :datetime not null
|
||||||
# conversation_id :integer not null
|
# account_id :integer not null
|
||||||
# inbox_id :integer not null
|
# conversation_id :integer not null
|
||||||
# sender_id :bigint
|
# inbox_id :integer not null
|
||||||
# source_id :string
|
# sender_id :bigint
|
||||||
|
# source_id :string
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
@@ -57,6 +58,7 @@ class Message < ApplicationRecord
|
|||||||
}.to_json.freeze
|
}.to_json.freeze
|
||||||
|
|
||||||
before_validation :ensure_content_type
|
before_validation :ensure_content_type
|
||||||
|
before_save :ensure_processed_message_content
|
||||||
|
|
||||||
validates :account_id, presence: true
|
validates :account_id, presence: true
|
||||||
validates :inbox_id, presence: true
|
validates :inbox_id, presence: true
|
||||||
@@ -214,6 +216,13 @@ class Message < ApplicationRecord
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def ensure_processed_message_content
|
||||||
|
text_content_quoted = content_attributes.dig(:email, :text_content, :quoted)
|
||||||
|
html_content_quoted = content_attributes.dig(:email, :html_content, :quoted)
|
||||||
|
|
||||||
|
self.processed_message_content = text_content_quoted || html_content_quoted || content
|
||||||
|
end
|
||||||
|
|
||||||
def ensure_content_type
|
def ensure_content_type
|
||||||
self.content_type ||= Message.content_types[:text]
|
self.content_type ||= Message.content_types[:text]
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class AutomationRules::ConditionsFilterService < FilterService
|
|||||||
end
|
end
|
||||||
|
|
||||||
records = base_relation.where(@query_string, @filter_values.with_indifferent_access)
|
records = base_relation.where(@query_string, @filter_values.with_indifferent_access)
|
||||||
|
|
||||||
records = perform_attribute_changed_filter(records) if @attribute_changed_query_filter.any?
|
records = perform_attribute_changed_filter(records) if @attribute_changed_query_filter.any?
|
||||||
|
|
||||||
records.any?
|
records.any?
|
||||||
@@ -93,6 +94,8 @@ class AutomationRules::ConditionsFilterService < FilterService
|
|||||||
attribute_key = query_hash['attribute_key']
|
attribute_key = query_hash['attribute_key']
|
||||||
query_operator = query_hash['query_operator']
|
query_operator = query_hash['query_operator']
|
||||||
|
|
||||||
|
attribute_key = 'processed_message_content' if attribute_key == 'content'
|
||||||
|
|
||||||
filter_operator_value = filter_operation(query_hash, current_index)
|
filter_operator_value = filter_operation(query_hash, current_index)
|
||||||
|
|
||||||
case current_filter['attribute_type']
|
case current_filter['attribute_type']
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
class AddProcessedMessageContentToMessages < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
add_column :messages, :processed_message_content, :text, null: true
|
||||||
|
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[7.0].define(version: 2023_05_23_104139) do
|
ActiveRecord::Schema[7.0].define(version: 2023_06_08_040738) 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"
|
||||||
enable_extension "pg_trgm"
|
enable_extension "pg_trgm"
|
||||||
@@ -663,6 +663,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_05_23_104139) do
|
|||||||
t.bigint "sender_id"
|
t.bigint "sender_id"
|
||||||
t.jsonb "external_source_ids", default: {}
|
t.jsonb "external_source_ids", default: {}
|
||||||
t.jsonb "additional_attributes", default: {}
|
t.jsonb "additional_attributes", default: {}
|
||||||
|
t.text "processed_message_content"
|
||||||
t.index "((additional_attributes -> 'campaign_id'::text))", name: "index_messages_on_additional_attributes_campaign_id", using: :gin
|
t.index "((additional_attributes -> 'campaign_id'::text))", name: "index_messages_on_additional_attributes_campaign_id", using: :gin
|
||||||
t.index ["account_id", "inbox_id"], name: "index_messages_on_account_id_and_inbox_id"
|
t.index ["account_id", "inbox_id"], name: "index_messages_on_account_id_and_inbox_id"
|
||||||
t.index ["account_id"], name: "index_messages_on_account_id"
|
t.index ["account_id"], name: "index_messages_on_account_id"
|
||||||
|
|||||||
@@ -505,51 +505,6 @@ describe AutomationRuleListener do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#message_created' do
|
|
||||||
before do
|
|
||||||
automation_rule.update!(
|
|
||||||
event_name: 'message_created',
|
|
||||||
name: 'Call actions message created',
|
|
||||||
description: 'Add labels, assign team after message created',
|
|
||||||
conditions: [{ 'values': ['incoming'], 'attribute_key': 'message_type', 'query_operator': nil, 'filter_operator': 'equal_to' }]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
let!(:message) { create(:message, account: account, conversation: conversation, message_type: 'incoming') }
|
|
||||||
let!(:event) do
|
|
||||||
Events::Base.new('message_created', Time.zone.now, { conversation: conversation, message: message })
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when rule matches' do
|
|
||||||
it 'triggers automation rule to assign team' do
|
|
||||||
expect(conversation.team_id).not_to eq(team.id)
|
|
||||||
expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation)
|
|
||||||
listener.message_created(event)
|
|
||||||
conversation.reload
|
|
||||||
|
|
||||||
expect(conversation.team_id).to eq(team.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'triggers automation rule to add label' do
|
|
||||||
expect(conversation.labels).to eq([])
|
|
||||||
expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation)
|
|
||||||
listener.message_created(event)
|
|
||||||
conversation.reload
|
|
||||||
|
|
||||||
expect(conversation.labels.pluck(:name)).to contain_exactly('support', 'priority_customer')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'triggers automation rule to assign best agent' do
|
|
||||||
expect(conversation.assignee).to be_nil
|
|
||||||
expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation)
|
|
||||||
listener.message_created(event)
|
|
||||||
conversation.reload
|
|
||||||
|
|
||||||
expect(conversation.assignee).to eq(user_1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#message_created with conversation and contacts based conditions' do
|
describe '#message_created with conversation and contacts based conditions' do
|
||||||
before do
|
before do
|
||||||
automation_rule.update!(
|
automation_rule.update!(
|
||||||
|
|||||||
@@ -164,6 +164,13 @@ describe AutomationRuleListener do
|
|||||||
listener.message_created(event)
|
listener.message_created(event)
|
||||||
expect(AutomationRules::ActionService).not_to have_received(:new).with(automation_rule, account, conversation)
|
expect(AutomationRules::ActionService).not_to have_received(:new).with(automation_rule, account, conversation)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'does not call AutomationRules::ActionService if conditions do not match based on content' do
|
||||||
|
message.update!(processed_message_content: 'hi', content: "hi\n\nhello")
|
||||||
|
allow(condition_match).to receive(:present?).and_return(false)
|
||||||
|
listener.message_created(event)
|
||||||
|
expect(AutomationRules::ActionService).not_to have_received(:new).with(automation_rule, account, conversation)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ RSpec.describe Message do
|
|||||||
message.inbox = create(:inbox, account: message.account, channel: build(:channel_email, account: message.account))
|
message.inbox = create(:inbox, account: message.account, channel: build(:channel_email, account: message.account))
|
||||||
allow(EmailReplyWorker).to receive(:perform_in).and_return(true)
|
allow(EmailReplyWorker).to receive(:perform_in).and_return(true)
|
||||||
message.message_type = 'outgoing'
|
message.message_type = 'outgoing'
|
||||||
|
message.content_attributes = { email: { text_content: { quoted: 'quoted text' } } }
|
||||||
message.save!
|
message.save!
|
||||||
expect(EmailReplyWorker).to have_received(:perform_in).with(1.second, message.id)
|
expect(EmailReplyWorker).to have_received(:perform_in).with(1.second, message.id)
|
||||||
end
|
end
|
||||||
@@ -223,6 +224,15 @@ RSpec.describe Message do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when processed_message_content is blank' do
|
||||||
|
let(:message) { build(:message, content_type: :text, account: create(:account), content: 'Processed message content') }
|
||||||
|
|
||||||
|
it 'sets content_type as text' do
|
||||||
|
message.save!
|
||||||
|
expect(message.processed_message_content).to eq message.content
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when attachments size maximum' do
|
context 'when attachments size maximum' do
|
||||||
let(:message) { build(:message, content_type: nil, account: create(:account)) }
|
let(:message) { build(:message, content_type: nil, account: create(:account)) }
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ require 'rails_helper'
|
|||||||
RSpec.describe AutomationRules::ConditionsFilterService do
|
RSpec.describe AutomationRules::ConditionsFilterService do
|
||||||
let(:account) { create(:account) }
|
let(:account) { create(:account) }
|
||||||
let(:conversation) { create(:conversation, account: account) }
|
let(:conversation) { create(:conversation, account: account) }
|
||||||
|
let(:email_channel) { create(:channel_email, account: account) }
|
||||||
|
let(:email_inbox) { create(:inbox, channel: email_channel, account: account) }
|
||||||
|
let(:message) do
|
||||||
|
create(:message, account: account, conversation: conversation, content: 'test text', inbox: conversation.inbox, message_type: :incoming)
|
||||||
|
end
|
||||||
let(:rule) { create(:automation_rule, account: account) }
|
let(:rule) { create(:automation_rule, account: account) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
@@ -57,5 +62,54 @@ RSpec.describe AutomationRules::ConditionsFilterService do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when conditions based on messages attributes' do
|
||||||
|
context 'when filter_operator is equal_to' do
|
||||||
|
before do
|
||||||
|
rule.conditions = [
|
||||||
|
{ 'values': ['test text'], 'attribute_key': 'content', 'query_operator': 'AND', 'filter_operator': 'equal_to' },
|
||||||
|
{ 'values': ['incoming'], 'attribute_key': 'message_type', 'query_operator': nil, 'filter_operator': 'equal_to' }
|
||||||
|
]
|
||||||
|
rule.save
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'will return true when conditions matches' do
|
||||||
|
expect(described_class.new(rule, conversation, { message: message, changed_attributes: {} }).perform).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'will return false when conditions in rule does not match' do
|
||||||
|
message.update!(message_type: :outgoing)
|
||||||
|
expect(described_class.new(rule, conversation, { message: message, changed_attributes: {} }).perform).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when filter_operator is on processed_message_content' do
|
||||||
|
before do
|
||||||
|
rule.conditions = [
|
||||||
|
{ 'values': ['help'], 'attribute_key': 'content', 'query_operator': 'AND', 'filter_operator': 'contains' },
|
||||||
|
{ 'values': ['incoming'], 'attribute_key': 'message_type', 'query_operator': nil, 'filter_operator': 'equal_to' }
|
||||||
|
]
|
||||||
|
rule.save
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:conversation) { create(:conversation, account: account, inbox: email_inbox) }
|
||||||
|
let(:message) do
|
||||||
|
create(:message, account: account, conversation: conversation, content: "We will help you\n\n\n test",
|
||||||
|
inbox: conversation.inbox, message_type: :incoming,
|
||||||
|
content_attributes: { email: { text_content: { quoted: 'We will help you' } } })
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'will return true for processed_message_content matches' do
|
||||||
|
message
|
||||||
|
expect(described_class.new(rule, conversation, { message: message, changed_attributes: {} }).perform).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'will return false when processed_message_content does no match' do
|
||||||
|
rule.update(conditions: [{ 'values': ['text'], 'attribute_key': 'content', 'query_operator': nil, 'filter_operator': 'contains' }])
|
||||||
|
|
||||||
|
expect(described_class.new(rule, conversation, { message: message, changed_attributes: {} }).perform).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user