Merge branch 'develop' into design/update-label-theme
This commit is contained in:
commit
5755a3804e
7 changed files with 158 additions and 41 deletions
|
@ -8,6 +8,7 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@automation_rule = Current.account.automation_rules.create(automation_rules_permit)
|
@automation_rule = Current.account.automation_rules.create(automation_rules_permit)
|
||||||
|
@automation_rule.update(actions: params[:actions])
|
||||||
end
|
end
|
||||||
|
|
||||||
def show; end
|
def show; end
|
||||||
|
|
|
@ -1,20 +1,33 @@
|
||||||
require 'json'
|
require 'json'
|
||||||
|
|
||||||
class AutomationRules::ConditionsFilterService < FilterService
|
class AutomationRules::ConditionsFilterService < FilterService
|
||||||
|
ATTRIBUTE_MODEL = 'contact_attribute'.freeze
|
||||||
|
|
||||||
def initialize(rule, conversation = nil)
|
def initialize(rule, conversation = nil)
|
||||||
super([], nil)
|
super([], nil)
|
||||||
@rule = rule
|
@rule = rule
|
||||||
@conversation = conversation
|
@conversation = conversation
|
||||||
|
@account = conversation.account
|
||||||
file = File.read('./lib/filters/filter_keys.json')
|
file = File.read('./lib/filters/filter_keys.json')
|
||||||
@filters = JSON.parse(file)
|
@filters = JSON.parse(file)
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
conversation_filters = @filters['conversations']
|
conversation_filters = @filters['conversations']
|
||||||
|
contact_filters = @filters['contacts']
|
||||||
|
|
||||||
@rule.conditions.each_with_index do |query_hash, current_index|
|
@rule.conditions.each_with_index do |query_hash, current_index|
|
||||||
current_filter = conversation_filters[query_hash['attribute_key']]
|
conversation_filter = conversation_filters[query_hash['attribute_key']]
|
||||||
@query_string += conversation_query_string(current_filter, query_hash.with_indifferent_access, current_index)
|
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
|
end
|
||||||
|
|
||||||
records = base_relation.where(@query_string, @filter_values.with_indifferent_access)
|
records = base_relation.where(@query_string, @filter_values.with_indifferent_access)
|
||||||
|
@ -57,25 +70,26 @@ class AutomationRules::ConditionsFilterService < FilterService
|
||||||
records.any?
|
records.any?
|
||||||
end
|
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']
|
attribute_key = query_hash['attribute_key']
|
||||||
query_operator = query_hash['query_operator']
|
query_operator = query_hash['query_operator']
|
||||||
|
|
||||||
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']
|
||||||
when 'additional_attributes'
|
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'
|
when 'standard'
|
||||||
if attribute_key == 'labels'
|
if attribute_key == 'labels'
|
||||||
" tags.id #{filter_operator_value} #{query_operator} "
|
" tags.id #{filter_operator_value} #{query_operator} "
|
||||||
else
|
else
|
||||||
" conversations.#{attribute_key} #{filter_operator_value} #{query_operator} "
|
" #{table_name}.#{attribute_key} #{filter_operator_value} #{query_operator} "
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
def base_relation
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,7 +26,7 @@ class Contacts::FilterService < FilterService
|
||||||
query_operator = query_hash[:query_operator]
|
query_operator = query_hash[:query_operator]
|
||||||
filter_operator_value = filter_operation(query_hash, current_index)
|
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']
|
case current_filter['attribute_type']
|
||||||
when 'additional_attributes'
|
when 'additional_attributes'
|
||||||
|
@ -64,18 +64,4 @@ class Contacts::FilterService < FilterService
|
||||||
|
|
||||||
"!= :value_#{current_index}"
|
"!= :value_#{current_index}"
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -32,7 +32,7 @@ class Conversations::FilterService < FilterService
|
||||||
query_operator = query_hash[:query_operator]
|
query_operator = query_hash[:query_operator]
|
||||||
filter_operator_value = filter_operation(query_hash, current_index)
|
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']
|
case current_filter['attribute_type']
|
||||||
when 'additional_attributes'
|
when 'additional_attributes'
|
||||||
|
@ -62,20 +62,4 @@ class Conversations::FilterService < FilterService
|
||||||
)
|
)
|
||||||
@conversations.latest.page(current_page)
|
@conversations.latest.page(current_page)
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -87,10 +87,26 @@ class FilterService
|
||||||
]
|
]
|
||||||
end
|
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
|
private
|
||||||
|
|
||||||
def custom_attribute(attribute_key)
|
def custom_attribute(attribute_key, account = nil)
|
||||||
@custom_attribute = Current.account.custom_attribute_definitions.where(
|
current_account = account || Current.account
|
||||||
|
@custom_attribute = current_account.custom_attribute_definitions.where(
|
||||||
attribute_model: self.class::ATTRIBUTE_MODEL
|
attribute_model: self.class::ATTRIBUTE_MODEL
|
||||||
).find_by(attribute_key: attribute_key)
|
).find_by(attribute_key: attribute_key)
|
||||||
end
|
end
|
||||||
|
|
|
@ -142,6 +142,13 @@
|
||||||
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain" ],
|
"filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain" ],
|
||||||
"attribute_type": "additional_attributes"
|
"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": {
|
"labels": {
|
||||||
"attribute_name": "Labels",
|
"attribute_name": "Labels",
|
||||||
"input_type": "tags",
|
"input_type": "tags",
|
||||||
|
|
|
@ -15,6 +15,12 @@ describe AutomationRuleListener do
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
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_1, team: team)
|
||||||
create(:team_member, user: user_2, team: team)
|
create(:team_member, user: user_2, team: team)
|
||||||
create(:account_user, user: user_2, account: account)
|
create(:account_user, user: user_2, account: account)
|
||||||
|
@ -77,6 +83,7 @@ describe AutomationRuleListener do
|
||||||
listener.conversation_status_changed(event)
|
listener.conversation_status_changed(event)
|
||||||
|
|
||||||
conversation.reload
|
conversation.reload
|
||||||
|
|
||||||
expect(conversation.labels.pluck(:name)).to contain_exactly('support', 'priority_customer')
|
expect(conversation.labels.pluck(:name)).to contain_exactly('support', 'priority_customer')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -146,6 +153,108 @@ describe AutomationRuleListener do
|
||||||
end
|
end
|
||||||
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
|
describe '#conversation_updated' do
|
||||||
before do
|
before do
|
||||||
automation_rule.update!(
|
automation_rule.update!(
|
||||||
|
|
Loading…
Reference in a new issue