Feat: automation rule based on contact conditions (#4230)

This commit is contained in:
Tejaswini Chile 2022-03-29 17:29:34 +05:30 committed by GitHub
parent c674393c02
commit 3158966241
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 158 additions and 41 deletions

View file

@ -8,6 +8,7 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont
def create
@automation_rule = Current.account.automation_rules.create(automation_rules_permit)
@automation_rule.update(actions: params[:actions])
end
def show; end

View file

@ -1,20 +1,33 @@
require 'json'
class AutomationRules::ConditionsFilterService < FilterService
ATTRIBUTE_MODEL = 'contact_attribute'.freeze
def initialize(rule, conversation = nil)
super([], nil)
@rule = rule
@conversation = conversation
@account = conversation.account
file = File.read('./lib/filters/filter_keys.json')
@filters = JSON.parse(file)
end
def perform
conversation_filters = @filters['conversations']
contact_filters = @filters['contacts']
@rule.conditions.each_with_index do |query_hash, current_index|
current_filter = conversation_filters[query_hash['attribute_key']]
@query_string += conversation_query_string(current_filter, query_hash.with_indifferent_access, current_index)
conversation_filter = conversation_filters[query_hash['attribute_key']]
contact_filter = contact_filters[query_hash['attribute_key']]
if conversation_filter
@query_string += conversation_query_string('conversations', conversation_filter, query_hash.with_indifferent_access, current_index)
elsif contact_filter
@query_string += conversation_query_string('contacts', contact_filter, query_hash.with_indifferent_access, current_index)
elsif custom_attribute(query_hash['attribute_key'], @account)
# send table name according to attribute key right now we are supporting contact based custom attribute filter
@query_string += custom_attribute_query(query_hash.with_indifferent_access, 'contacts', current_index)
end
end
records = base_relation.where(@query_string, @filter_values.with_indifferent_access)
@ -57,25 +70,26 @@ class AutomationRules::ConditionsFilterService < FilterService
records.any?
end
def conversation_query_string(current_filter, query_hash, current_index)
def conversation_query_string(table_name, current_filter, query_hash, current_index)
attribute_key = query_hash['attribute_key']
query_operator = query_hash['query_operator']
filter_operator_value = filter_operation(query_hash, current_index)
case current_filter['attribute_type']
when 'additional_attributes'
" conversations.additional_attributes ->> '#{attribute_key}' #{filter_operator_value} #{query_operator} "
" #{table_name}.additional_attributes ->> '#{attribute_key}' #{filter_operator_value} #{query_operator} "
when 'standard'
if attribute_key == 'labels'
" tags.id #{filter_operator_value} #{query_operator} "
else
" conversations.#{attribute_key} #{filter_operator_value} #{query_operator} "
" #{table_name}.#{attribute_key} #{filter_operator_value} #{query_operator} "
end
end
end
private
def base_relation
Conversation.where(id: @conversation.id)
Conversation.where(id: @conversation.id).joins('LEFT OUTER JOIN contacts on conversations.contact_id = contacts.id')
end
end

View file

@ -26,7 +26,7 @@ class Contacts::FilterService < FilterService
query_operator = query_hash[:query_operator]
filter_operator_value = filter_operation(query_hash, current_index)
return custom_attribute_query(query_hash, current_index) if current_filter.nil?
return custom_attribute_query(query_hash, 'contacts', current_index) if current_filter.nil?
case current_filter['attribute_type']
when 'additional_attributes'
@ -64,18 +64,4 @@ class Contacts::FilterService < FilterService
"!= :value_#{current_index}"
end
def custom_attribute_query(query_hash, current_index)
attribute_key = query_hash[:attribute_key]
query_operator = query_hash[:query_operator]
attribute_type = custom_attribute(attribute_key).try(:attribute_display_type)
filter_operator_value = filter_operation(query_hash, current_index)
attribute_data_type = self.class::ATTRIBUTE_TYPES[attribute_type]
if custom_attribute(attribute_key)
" LOWER(contacts.custom_attributes ->> '#{attribute_key}')::#{attribute_data_type} #{filter_operator_value} #{query_operator} "
else
' '
end
end
end

View file

@ -32,7 +32,7 @@ class Conversations::FilterService < FilterService
query_operator = query_hash[:query_operator]
filter_operator_value = filter_operation(query_hash, current_index)
return custom_attribute_query(query_hash, current_index) if current_filter.nil?
return custom_attribute_query(query_hash, 'conversations', current_index) if current_filter.nil?
case current_filter['attribute_type']
when 'additional_attributes'
@ -62,20 +62,4 @@ class Conversations::FilterService < FilterService
)
@conversations.latest.page(current_page)
end
private
def custom_attribute_query(query_hash, current_index)
attribute_key = query_hash[:attribute_key]
query_operator = query_hash[:query_operator]
attribute_type = custom_attribute(attribute_key).try(:attribute_display_type)
filter_operator_value = filter_operation(query_hash, current_index)
attribute_data_type = self.class::ATTRIBUTE_TYPES[attribute_type]
if custom_attribute(attribute_key)
" LOWER(conversations.custom_attributes ->> '#{attribute_key}')::#{attribute_data_type} #{filter_operator_value} #{query_operator} "
else
' '
end
end
end

View file

@ -87,10 +87,26 @@ class FilterService
]
end
def custom_attribute_query(query_hash, table_name, current_index)
attribute_key = query_hash[:attribute_key]
query_operator = query_hash[:query_operator]
attribute_type = custom_attribute(attribute_key, @account).try(:attribute_display_type)
filter_operator_value = filter_operation(query_hash, current_index)
attribute_data_type = self.class::ATTRIBUTE_TYPES[attribute_type]
if custom_attribute(attribute_key, @account)
" LOWER(#{table_name}.custom_attributes ->> '#{attribute_key}')::#{attribute_data_type} #{filter_operator_value} #{query_operator} "
else
' '
end
end
private
def custom_attribute(attribute_key)
@custom_attribute = Current.account.custom_attribute_definitions.where(
def custom_attribute(attribute_key, account = nil)
current_account = account || Current.account
@custom_attribute = current_account.custom_attribute_definitions.where(
attribute_model: self.class::ATTRIBUTE_MODEL
).find_by(attribute_key: attribute_key)
end

View file

@ -142,6 +142,13 @@
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain" ],
"attribute_type": "additional_attributes"
},
"company": {
"attribute_name": "Company",
"input_type": "textbox",
"data_type": "text",
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain" ],
"attribute_type": "additional_attributes"
},
"labels": {
"attribute_name": "Labels",
"input_type": "tags",

View file

@ -15,6 +15,12 @@ describe AutomationRuleListener do
end
before do
create(:custom_attribute_definition,
attribute_key: 'customer_type',
account: account,
attribute_model: 'contact_attribute',
attribute_display_type: 'list',
attribute_values: %w[regular platinum gold])
create(:team_member, user: user_1, team: team)
create(:team_member, user: user_2, team: team)
create(:account_user, user: user_2, account: account)
@ -77,6 +83,7 @@ describe AutomationRuleListener do
listener.conversation_status_changed(event)
conversation.reload
expect(conversation.labels.pluck(:name)).to contain_exactly('support', 'priority_customer')
end
@ -146,6 +153,108 @@ describe AutomationRuleListener do
end
end
describe '#conversation_updated with contacts attributes' do
before do
conversation.contact.update!(custom_attributes: { customer_type: 'platinum', signed_in_at: '2022-01-19' },
additional_attributes: { 'company': 'Marvel' })
automation_rule.update!(
event_name: 'conversation_updated',
name: 'Call actions conversation updated',
description: 'Add labels, assign team after conversation updated',
conditions: [
{
attribute_key: 'company',
filter_operator: 'equal_to',
values: ['Marvel'],
query_operator: 'AND'
}.with_indifferent_access,
{
attribute_key: 'customer_type',
filter_operator: 'equal_to',
values: ['platinum'],
query_operator: nil
}.with_indifferent_access
]
)
end
let!(:event) do
Events::Base.new('conversation_updated', Time.zone.now, { conversation: conversation })
end
context 'when rule matches with additional_attributes and custom_attributes' do
it 'triggers automation rule to assign team' do
expect(conversation.team_id).not_to eq(team.id)
listener.conversation_updated(event)
conversation.reload
expect(conversation.team_id).to eq(team.id)
end
it 'triggers automation rule to add label and assign best agents' do
expect(conversation.labels).to eq([])
expect(conversation.assignee).to be_nil
listener.conversation_updated(event)
conversation.reload
expect(conversation.labels.pluck(:name)).to contain_exactly('support', 'priority_customer')
expect(conversation.assignee).to eq(user_1)
end
it 'triggers automation rule send email transcript to the mentioned email' do
mailer = double
expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_updated)
listener.conversation_updated(event)
conversation.reload
allow(mailer).to receive(:conversation_transcript)
end
it 'triggers automation rule send message to the contacts' do
expect(conversation.messages).to be_empty
expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_updated)
listener.conversation_updated(event)
conversation.reload
expect(conversation.messages.last.content).to eq('Send this message.')
end
it 'triggers automation_rule with contact standard attributes' do
automation_rule.update!(
conditions: [
{
attribute_key: 'email',
filter_operator: 'contains',
values: ['example.com'],
query_operator: 'AND'
}.with_indifferent_access,
{
attribute_key: 'customer_type',
filter_operator: 'equal_to',
values: ['platinum'],
query_operator: nil
}.with_indifferent_access
]
)
expect(conversation.team_id).not_to eq(team.id)
listener.conversation_updated(event)
conversation.reload
expect(conversation.team_id).to eq(team.id)
end
end
end
describe '#conversation_updated' do
before do
automation_rule.update!(