feat: Attribute changed filter for automations (#4621)
This commit is contained in:
parent
267252d13a
commit
56f668db6b
4 changed files with 148 additions and 6 deletions
|
@ -9,6 +9,7 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont
|
||||||
def create
|
def create
|
||||||
@automation_rule = Current.account.automation_rules.new(automation_rules_permit)
|
@automation_rule = Current.account.automation_rules.new(automation_rules_permit)
|
||||||
@automation_rule.actions = params[:actions]
|
@automation_rule.actions = params[:actions]
|
||||||
|
@automation_rule.conditions = params[:conditions]
|
||||||
|
|
||||||
render json: { error: @automation_rule.errors.messages }, status: :unprocessable_entity and return unless @automation_rule.valid?
|
render json: { error: @automation_rule.errors.messages }, status: :unprocessable_entity and return unless @automation_rule.valid?
|
||||||
|
|
||||||
|
@ -31,9 +32,7 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont
|
||||||
|
|
||||||
def update
|
def update
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
@automation_rule.update!(automation_rules_permit)
|
automation_rule_update
|
||||||
@automation_rule.actions = params[:actions] if params[:actions]
|
|
||||||
@automation_rule.save!
|
|
||||||
process_attachments
|
process_attachments
|
||||||
|
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
|
@ -67,6 +66,13 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def automation_rule_update
|
||||||
|
@automation_rule.update!(automation_rules_permit)
|
||||||
|
@automation_rule.actions = params[:actions] if params[:actions]
|
||||||
|
@automation_rule.conditions = params[:conditions] if params[:conditions]
|
||||||
|
@automation_rule.save!
|
||||||
|
end
|
||||||
|
|
||||||
def automation_rules_permit
|
def automation_rules_permit
|
||||||
params.permit(
|
params.permit(
|
||||||
:name, :description, :event_name, :account_id, :active,
|
:name, :description, :event_name, :account_id, :active,
|
||||||
|
|
|
@ -4,11 +4,12 @@ class AutomationRuleListener < BaseListener
|
||||||
|
|
||||||
conversation = event_obj.data[:conversation]
|
conversation = event_obj.data[:conversation]
|
||||||
account = conversation.account
|
account = conversation.account
|
||||||
|
changed_attributes = event_obj.data[:changed_attributes]
|
||||||
|
|
||||||
return unless rule_present?('conversation_updated', account)
|
return unless rule_present?('conversation_updated', account)
|
||||||
|
|
||||||
@rules.each do |rule|
|
@rules.each do |rule|
|
||||||
conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation).perform
|
conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation, { changed_attributes: changed_attributes }).perform
|
||||||
AutomationRules::ActionService.new(rule, account, conversation).perform if conditions_match.present?
|
AutomationRules::ActionService.new(rule, account, conversation).perform if conditions_match.present?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -18,11 +19,12 @@ class AutomationRuleListener < BaseListener
|
||||||
|
|
||||||
conversation = event_obj.data[:conversation]
|
conversation = event_obj.data[:conversation]
|
||||||
account = conversation.account
|
account = conversation.account
|
||||||
|
changed_attributes = event_obj.data[:changed_attributes]
|
||||||
|
|
||||||
return unless rule_present?('conversation_created', account)
|
return unless rule_present?('conversation_created', account)
|
||||||
|
|
||||||
@rules.each do |rule|
|
@rules.each do |rule|
|
||||||
conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation).perform
|
conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation, { changed_attributes: changed_attributes }).perform
|
||||||
::AutomationRules::ActionService.new(rule, account, conversation).perform if conditions_match.present?
|
::AutomationRules::ActionService.new(rule, account, conversation).perform if conditions_match.present?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -32,11 +34,13 @@ class AutomationRuleListener < BaseListener
|
||||||
|
|
||||||
message = event_obj.data[:message]
|
message = event_obj.data[:message]
|
||||||
account = message.try(:account)
|
account = message.try(:account)
|
||||||
|
changed_attributes = event_obj.data[:changed_attributes]
|
||||||
|
|
||||||
return unless rule_present?('message_created', account)
|
return unless rule_present?('message_created', account)
|
||||||
|
|
||||||
@rules.each do |rule|
|
@rules.each do |rule|
|
||||||
conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, message.conversation, { message: message }).perform
|
conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, message.conversation,
|
||||||
|
{ message: message, changed_attributes: changed_attributes }).perform
|
||||||
::AutomationRules::ActionService.new(rule, account, message.conversation).perform if conditions_match.present?
|
::AutomationRules::ActionService.new(rule, account, message.conversation).perform if conditions_match.present?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,18 +11,24 @@ class AutomationRules::ConditionsFilterService < FilterService
|
||||||
file = File.read('./lib/filters/filter_keys.json')
|
file = File.read('./lib/filters/filter_keys.json')
|
||||||
@filters = JSON.parse(file)
|
@filters = JSON.parse(file)
|
||||||
@options = options
|
@options = options
|
||||||
|
@changed_attributes = options[:changed_attributes]
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
@conversation_filters = @filters['conversations']
|
@conversation_filters = @filters['conversations']
|
||||||
@contact_filters = @filters['contacts']
|
@contact_filters = @filters['contacts']
|
||||||
@message_filters = @filters['messages']
|
@message_filters = @filters['messages']
|
||||||
|
@attribute_changed_query_filter = []
|
||||||
|
|
||||||
@rule.conditions.each_with_index do |query_hash, current_index|
|
@rule.conditions.each_with_index do |query_hash, current_index|
|
||||||
|
@attribute_changed_query_filter << query_hash and next if query_hash['filter_operator'] == 'attribute_changed'
|
||||||
|
|
||||||
apply_filter(query_hash, current_index)
|
apply_filter(query_hash, current_index)
|
||||||
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.any?
|
records.any?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -43,6 +49,37 @@ class AutomationRules::ConditionsFilterService < FilterService
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# If attribute_changed type filter is present perform this against array
|
||||||
|
def perform_attribute_changed_filter(records)
|
||||||
|
@attribute_changed_records = []
|
||||||
|
current_attribute_changed_record = base_relation
|
||||||
|
filter_based_on_attribute_change(records, current_attribute_changed_record)
|
||||||
|
|
||||||
|
@attribute_changed_records.uniq
|
||||||
|
end
|
||||||
|
|
||||||
|
# Loop through attribute_changed_query_filter
|
||||||
|
def filter_based_on_attribute_change(records, current_attribute_changed_record)
|
||||||
|
@attribute_changed_query_filter.each do |filter|
|
||||||
|
@changed_attributes = @changed_attributes.with_indifferent_access
|
||||||
|
changed_attribute = @changed_attributes[filter['attribute_key']].presence
|
||||||
|
|
||||||
|
if changed_attribute[0].in?(filter['values']['from']) && changed_attribute[1].in?(filter['values']['to'])
|
||||||
|
@attribute_changed_records = attribute_changed_filter_query(filter, records, current_attribute_changed_record)
|
||||||
|
end
|
||||||
|
current_attribute_changed_record = @attribute_changed_records
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# We intersect with the record if query_operator-AND is present and union if query_operator-OR is present
|
||||||
|
def attribute_changed_filter_query(filter, records, current_attribute_changed_record)
|
||||||
|
if filter['query_operator'] == 'AND'
|
||||||
|
@attribute_changed_records + (current_attribute_changed_record & records)
|
||||||
|
else
|
||||||
|
@attribute_changed_records + (current_attribute_changed_record | records)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def message_query_string(current_filter, query_hash, current_index)
|
def message_query_string(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']
|
||||||
|
|
|
@ -259,6 +259,101 @@ describe AutomationRuleListener do
|
||||||
expect(conversation.messages.first.content).to eq('Send this message.')
|
expect(conversation.messages.first.content).to eq('Send this message.')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when conditions based on attribute_changed' do
|
||||||
|
before do
|
||||||
|
automation_rule.update!(
|
||||||
|
event_name: 'conversation_updated',
|
||||||
|
name: 'Call actions conversation updated when company changed from DC to Marvel',
|
||||||
|
description: 'Add labels, assign team after conversation updated',
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
attribute_key: 'company',
|
||||||
|
filter_operator: 'attribute_changed',
|
||||||
|
values: { from: ['DC'], to: ['Marvel'] },
|
||||||
|
query_operator: 'AND'
|
||||||
|
}.with_indifferent_access,
|
||||||
|
{
|
||||||
|
attribute_key: 'status',
|
||||||
|
filter_operator: 'equal_to',
|
||||||
|
values: ['snoozed'],
|
||||||
|
query_operator: nil
|
||||||
|
}.with_indifferent_access
|
||||||
|
]
|
||||||
|
)
|
||||||
|
conversation.update(status: :snoozed)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:event) do
|
||||||
|
Events::Base.new('conversation_updated', Time.zone.now, { conversation: conversation, changed_attributes: {
|
||||||
|
company: %w[DC Marvel]
|
||||||
|
} })
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when rule matches' 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 assign team with OR operator' do
|
||||||
|
conversation.update(status: :open)
|
||||||
|
automation_rule.update!(
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
attribute_key: 'company',
|
||||||
|
filter_operator: 'attribute_changed',
|
||||||
|
values: { from: ['DC'], to: ['Marvel'] },
|
||||||
|
query_operator: 'OR'
|
||||||
|
}.with_indifferent_access,
|
||||||
|
{
|
||||||
|
attribute_key: 'status',
|
||||||
|
filter_operator: 'equal_to',
|
||||||
|
values: ['snoozed'],
|
||||||
|
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
|
||||||
|
|
||||||
|
context 'when rule doesnt match' do
|
||||||
|
it 'when automation rule is triggered it will not assign team' do
|
||||||
|
conversation.update(status: :open)
|
||||||
|
|
||||||
|
expect(conversation.team_id).not_to eq(team.id)
|
||||||
|
|
||||||
|
listener.conversation_updated(event)
|
||||||
|
|
||||||
|
conversation.reload
|
||||||
|
expect(conversation.team_id).not_to eq(team.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'when automation rule is triggers, it will not assign team on attribute_changed values' do
|
||||||
|
conversation.update(status: :snoozed)
|
||||||
|
event = Events::Base.new('conversation_updated', Time.zone.now, { conversation: conversation,
|
||||||
|
changed_attributes: { company: %w[Marvel DC] } })
|
||||||
|
|
||||||
|
expect(conversation.team_id).not_to eq(team.id)
|
||||||
|
|
||||||
|
listener.conversation_updated(event)
|
||||||
|
|
||||||
|
conversation.reload
|
||||||
|
expect(conversation.team_id).not_to eq(team.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#message_created' do
|
describe '#message_created' do
|
||||||
|
|
Loading…
Reference in a new issue